home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
1843
/
1843.xpi
/
content
/
firebug
/
lib.js
< prev
next >
Wrap
Text File
|
2010-01-15
|
170KB
|
6,773 lines
/* See license.txt for terms of usage */
var FBL = fbXPCOMUtils;
try { /*@explore*/
(function() {
// ************************************************************************************************
// Modules
Components.utils.import("resource://gre/modules/PluralForm.jsm");
// ************************************************************************************************
// Constants
const Cc = Components.classes;
const Ci = Components.interfaces;
this.fbs = Cc["@joehewitt.com/firebug;1"].getService().wrappedJSObject;
this.httpObserver = this.CCSV("@joehewitt.com/firebug-http-observer;1", "nsIObserverService");
this.jsd = this.CCSV("@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");
const finder = this.finder = this.CCIN("@mozilla.org/embedcomp/rangefind;1", "nsIFind");
const wm = this.CCSV("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
const ioService = this.CCSV("@mozilla.org/network/io-service;1", "nsIIOService");
const consoleService = Components.classes["@mozilla.org/consoleservice;1"].
getService(Components.interfaces["nsIConsoleService"]);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
const reNotWhitespace = /[^\s]/;
const reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
const reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/; // This RE and the previous one should changed to be consistent
const reChromeCase = /chrome:\/\/([^/]*)\/(.*?)$/;
// Globals
this.reDataURL = /data:text\/javascript;fileName=([^;]*);baseLineNumber=(\d*?),((?:.*?%0A)|(?:.*))/g;
this.reJavascript = /\s*javascript:\s*(.*)/;
this.reChrome = /chrome:\/\/([^\/]*)\//;
this.reCSS = /\.css$/;
this.reFile = /file:\/\/([^\/]*)\//;
this.reUpperCase = /[A-Z]/;
const reSplitLines = /\r\n|\r|\n/;
const reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
const reGuessFunction = /['"]?([$0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
const reWord = /([A-Za-z_$][A-Za-z_$0-9]*)(\.([A-Za-z_$][A-Za-z_$0-9]*))*/;
const overrideDefaultsWithPersistedValuesTimeout = 500;
const NS_SEEK_SET = Ci.nsISeekableStream.NS_SEEK_SET;
// ************************************************************************************************
// Namespaces
var namespaces = [];
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.ns = function(fn)
{
var ns = {};
namespaces.push(fn, ns);
return ns;
};
this.initialize = function()
{
for (var i = 0; i < namespaces.length; i += 2)
{
var fn = namespaces[i];
var ns = namespaces[i+1];
fn.apply(ns);
}
};
// ************************************************************************************************
// Basics
this.bind = function() // fn, thisObject, args => thisObject.fn(args, arguments);
{
var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); }
};
this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args);
{
var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
return function() { return fn.apply(object, args); }
};
this.extend = function(l, r)
{
var newOb = {};
for (var n in l)
newOb[n] = l[n];
for (var n in r)
newOb[n] = r[n];
return newOb;
};
this.descend = function(prototypeParent, childProperties)
{
function protoSetter() {};
protoSetter.prototype = prototypeParent;
var newOb = new protoSetter();
for (var n in childProperties)
newOb[n] = childProperties[n];
return newOb;
};
// ************************************************************************************************
// Arrays
this.keys = function(map) // At least sometimes the keys will be on user-level window objects
{
var keys = [];
try
{
for (var name in map) // enumeration is safe
keys.push(name); // name is string, safe
}
catch (exc)
{
// Sometimes we get exceptions trying to iterate properties
}
return keys; // return is safe
};
this.values = function(map)
{
var values = [];
try
{
for (var name in map)
{
try
{
values.push(map[name]);
}
catch (exc)
{
// Sometimes we get exceptions trying to access properties
}
}
}
catch (exc)
{
// Sometimes we get exceptions trying to iterate properties
}
return values;
};
this.remove = function(list, item)
{
for (var i = 0; i < list.length; ++i)
{
if (list[i] == item)
{
list.splice(i, 1);
break;
}
}
};
this.sliceArray = function(array, index)
{
var slice = [];
for (var i = index; i < array.length; ++i)
slice.push(array[i]);
return slice;
};
function cloneArray(array, fn)
{
var newArray = [];
if (fn)
for (var i = 0; i < array.length; ++i)
newArray.push(fn(array[i]));
else
for (var i = 0; i < array.length; ++i)
newArray.push(array[i]);
return newArray;
}
function extendArray(array, array2)
{
var newArray = [];
newArray.push.apply(newArray, array);
newArray.push.apply(newArray, array2);
return newArray;
}
this.extendArray = extendArray;
this.cloneArray = cloneArray;
function arrayInsert(array, index, other)
{
for (var i = 0; i < other.length; ++i)
array.splice(i+index, 0, other[i]);
return array;
}
this.arrayInsert = arrayInsert;
// ************************************************************************************************
this.safeToString = function(ob)
{
try
{
if (!ob)
{
if (ob == undefined)
return 'undefined';
if (ob == null)
return 'null';
if (ob == false)
return 'false';
return "";
}
if (ob && (typeof (ob['toString']) == "function") )
return ob.toString();
if (ob && typeof (ob['toSource']) == 'function')
return ob.toSource();
/* https://bugzilla.mozilla.org/show_bug.cgi?id=522590 */
var str = "[";
for (var p in ob)
str += p+',';
return str + ']';
}
catch (exc)
{
}
return "[unsupported: no toString() function in type "+typeof(ob)+"]";
};
// ************************************************************************************************
this.hasProperties = function(ob)
{
try
{
for (var name in ob)
return true;
} catch (exc) {}
return false;
};
// ************************************************************************************************
this.convertToUnicode = function(text, charset)
{
if (!text)
return "";
try
{
var conv = this.CCSV("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
conv.charset = charset ? charset : "UTF-8";
return conv.ConvertToUnicode(text);
}
catch (exc)
{
throw new Error("Firebug failed to convert to unicode using charset: "+conv.charset+" in @mozilla.org/intl/scriptableunicodeconverter");
}
};
this.convertFromUnicode = function(text, charset)
{
if (!text)
return "";
try
{
var conv = this.CCSV("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
conv.charset = charset ? charset : "UTF-8";
return conv.ConvertFromUnicode(text);
}
catch (exc)
{
}
};
this.getPlatformName = function()
{
return this.CCSV("@mozilla.org/xre/app-info;1", "nsIXULRuntime").OS;
};
this.beep = function()
{
var sounder = this.CCSV("@mozilla.org/sound;1", "nsISound");
sounder.beep();
};
this.getUniqueId = function() {
return this.getRandomInt(0,65536);
}
this.getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// ************************************************************************************************
this.createStyleSheet = function(doc, url)
{
var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("charset","utf-8");
style.setAttribute("type", "text/css");
style.innerHTML = this.getResource(url);
FBL.unwrapObject(style).firebugIgnore = true;
return style;
}
this.addStyleSheet = function(doc, style)
{
var heads = doc.getElementsByTagName("head");
if (heads.length)
heads[0].appendChild(style);
else
doc.documentElement.appendChild(style);
};
this.addScript = function(doc, id, src)
{
var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "html:script");
element.setAttribute("type", "text/javascript");
element.setAttribute("id", id);
if (!FBTrace.DBG_CONSOLE)
FBL.unwrapObject(element).firebugIgnore = true;
element.innerHTML = src;
if (doc.documentElement)
doc.documentElement.appendChild(element);
else
{
// See issue 1079, the svg test case gives this error
}
return element;
}
// ************************************************************************************************
this.isAncestorIgnored = function(node)
{
for (var parent = node; parent; parent = parent.parentNode)
{
if (FBL.unwrapObject(parent).firebugIgnore)
return true;
}
return false;
}
// ************************************************************************************************
// Localization
/*
* $STR - intended for localization of a static string.
* $STRF - intended for localization of a string with dynamically inserted values.
* $STRP - intended for localization of a string with dynamically plural forms.
*
* Notes:
* 1) Name with _ in place of spaces is the key in the firebug.properties file.
* 2) If the specified key isn't localized for particular language, both methods use
* the part after the last dot (in the specified name) as the return value.
*
* Examples:
* $STR("Label"); - search for key "Label" within the firebug.properties file
* and returns its value. If the key doesn't exist returns "Label".
*
* $STR("Button Label"); - search for key "Button_Label" withing the firebug.properties
* file. If the key doesn't exist returns "Button Label".
*
* $STR("net.Response Header"); - search for key "net.Response_Header". If the key doesn't
* exist returns "Response Header".
*
* firebug.properties:
* net.timing.Request_Time=Request Time: %S [%S]
*
* var param1 = 10;
* var param2 = "ms";
* $STRF("net.timing.Request Time", param1, param2); -> "Request Time: 10 [ms]"
*
* - search for key "net.timing.Request_Time" within the firebug.properties file. Parameters
* are inserted at specified places (%S) in the same order as they are passed. If the
* key doesn't exist the method returns "Request Time".
*/
function $STR(name, bundle)
{
try
{
if (typeof bundle == "string")
bundle = document.getElementById(bundle);
if (bundle)
return bundle.getString(name.replace(' ', '_', "g"));
if (Firebug)
return Firebug.getStringBundle().GetStringFromName(name.replace(' ', '_', "g"));
}
catch (err)
{
}
// XXXjjb apparently we get to this code if we get an exception above...is that best we can do?
// Use only the label after last dot.
var index = name.lastIndexOf(".");
if (index > 0 && name.charAt(index-1) != "\\")
name = name.substr(index + 1);
name = name.replace("_", " ");
return name;
}
function $STRF(name, args, bundle)
{
try
{
// xxxHonza: Workaround for #485511
if (!bundle)
bundle = "strings_firebug";
if (typeof bundle == "string")
bundle = document.getElementById(bundle);
if (bundle)
return bundle.getFormattedString(name.replace(' ', '_', "g"), args);
else
return Firebug.getStringBundle().formatStringFromName(name.replace(' ', '_', "g"), args, args.length);
}
catch (err)
{
}
// Use only the label after last dot.
var index = name.lastIndexOf(".");
if (index > 0)
name = name.substr(index + 1);
return name;
}
function $STRP(name, args, index, bundle)
{
// xxxHonza:
// pluralRule from chrome://global/locale/intl.properties for Chinese is 1,
// which is wrong, it should be 0.
var getPluralForm = PluralForm.get;
var getNumForms = PluralForm.numForms;
// Get custom plural rule; otherwise the rule from chrome://global/locale/intl.properties
// (depends on the current locale) is used.
var pluralRule = Firebug.getPluralRule();
if (!isNaN(parseInt(pluralRule, 10)))
[getPluralForm, getNumForms] = PluralForm.makeGetter(pluralRule);
// Index of the argument with plural form (there must be only one arg that needs plural form).
if (!index)
index = 0;
var margs = [];
var numForms = getNumForms();
// Repeat the args for numForms time(s)
for (var i = 0; i < numForms; i++)
margs = margs.concat(args);
// Get proper plural form from the string (depends on the current Firefox locale).
var translatedString = $STRF(name, margs, bundle);
if (translatedString.search(";") > 0)
return getPluralForm(args[index], translatedString);
// translatedString contains no ";", either rule 0 or getString fails
return translatedString;
}
this.$STR = $STR;
this.$STRF = $STRF;
this.$STRP = $STRP;
/*
* Use the current value of the attribute as a key to look up the localized value.
*/
this.internationalize = function(element, attr, args)
{
if (typeof element == "string")
element = document.getElementById(element);
if (element)
{
var xulString = element.getAttribute(attr);
if (xulString)
{
var localized = args ? $STRF(xulString, args) : $STR(xulString);
// Set localized value of the attribute.
element.setAttribute(attr, localized);
}
}
else
{
}
}
// ************************************************************************************************
// Visibility
this.isVisible = function(elt)
{
if (isElementXUL(elt))
{
//FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
return (!elt.hidden && !elt.collapsed);
}
return elt.offsetWidth > 0 || elt.offsetHeight > 0 || elt.localName in invisibleTags || isElementSVG(elt) || isElementMathML(elt);
};
this.collapse = function(elt, collapsed)
{
elt.setAttribute("collapsed", collapsed ? "true" : "false");
};
this.obscure = function(elt, obscured)
{
if (obscured)
this.setClass(elt, "obscured");
else
this.removeClass(elt, "obscured");
};
this.hide = function(elt, hidden)
{
elt.style.visibility = hidden ? "hidden" : "visible";
};
this.clearNode = function(node)
{
this.clearDomplate(node);
node.innerHTML = "";
};
this.eraseNode = function(node)
{
this.clearDomplate(node);
while (node.lastChild)
node.removeChild(node.lastChild);
};
this.clearDomplate = function(node)
{
if (!Firebug.clearDomplate)
return;
var walker = node.ownerDocument.createTreeWalker(node,
Ci.nsIDOMNodeFilter.SHOW_ALL, null, true);
while (node)
{
if (node.repObject)
node.repObject = null;
if (node.stackTrace)
node.stackTrace = null;
if (node.checked)
node.checked = null;
if (node.domObject)
node.domObject = null;
if (node.toggles)
node.toggles = null;
if (node.domPanel)
node.domPanel = null;
node = walker.nextNode();
}
}
// ************************************************************************************************
// Window iteration
this.iterateWindows = function(win, handler)
{
if (!win || !win.document)
return;
handler(win);
if (win == top || !win.frames) return; // XXXjjb hack for chromeBug
for (var i = 0; i < win.frames.length; ++i)
{
var subWin = win.frames[i];
if (subWin != win)
this.iterateWindows(subWin, handler);
}
};
this.getRootWindow = function(win)
{
for (; win; win = win.parent)
{
if (!win.parent || win == win.parent || !(win.parent instanceof Window) )
return win;
}
return null;
};
// ************************************************************************************************
// CSS classes
var classNameReCache={};
this.hasClass = function(node, name)
{
if (!node || node.nodeType != 1 || !node.className || name == '')
return false;
if (name.indexOf(" ") != -1)
{
var classes = name.split(" "), len = classes.length, found=false;
for (var i = 0; i < len; i++)
{
var cls = classes[i].trim();
if (cls != "")
{
if (this.hasClass(node, cls) == false)
return false;
found = true;
}
}
return found;
}
var re;
if (name.indexOf("-") == -1)
re = classNameReCache[name] = classNameReCache[name] || new RegExp('(^|\\s)' + name + '(\\s|$)', "g");
else // XXXsroussey don't cache these, they are often setting values. Should be using setUserData/getUserData???
re = new RegExp('(^|\\s)' + name + '(\\s|$)', "g")
return node.className.search(re) != -1;
};
this.setClass = function(node, name)
{
if (!node || node.nodeType != 1 || name == '')
return;
if (name.indexOf(" ") != -1)
{
var classes = name.split(" "), len = classes.length;
for (var i = 0; i < len; i++)
{
var cls = classes[i].trim();
if (cls != "")
{
this.setClass(node, cls);
}
}
return;
}
if (!this.hasClass(node, name))
node.className = node.className.trim() + " " + name;
};
this.getClassValue = function(node, name)
{
var re = new RegExp(name+"-([^ ]+)");
var m = re.exec(node.className);
return m ? m[1] : "";
};
this.removeClass = function(node, name)
{
if (!node || node.nodeType != 1 || node.className == '' || name == '')
return;
if (name.indexOf(" ") != -1)
{
var classes = name.split(" "), len = classes.length;
for (var i = 0; i < len; i++)
{
var cls = classes[i].trim();
if (cls != "")
{
if (this.hasClass(node, cls) == false)
this.removeClass(node, cls);
}
}
return;
}
var re;
if (name.indexOf("-") == -1)
re = classNameReCache[name] = classNameReCache[name] || new RegExp('(^|\\s)' + name + '(\\s|$)', "g");
else // XXXsroussey don't cache these, they are often setting values. Should be using setUserData/getUserData???
re = new RegExp('(^|\\s)' + name + '(\\s|$)', "g")
node.className = node.className.replace(re, " ");
};
this.toggleClass = function(elt, name)
{
if (this.hasClass(elt, name))
this.removeClass(elt, name);
else
this.setClass(elt, name);
};
this.setClassTimed = function(elt, name, context, timeout)
{
if (!timeout)
timeout = 1300;
if (elt.__setClassTimeout)
context.clearTimeout(elt.__setClassTimeout);
else
this.setClass(elt, name);
if (!this.isVisible(elt))
{
if (elt.__invisibleAtSetPoint)
elt.__invisibleAtSetPoint--;
else
elt.__invisibleAtSetPoint = 5;
}
else
{
delete elt.__invisibleAtSetPoint;
}
elt.__setClassTimeout = context.setTimeout(function()
{
delete elt.__setClassTimeout;
if (elt.__invisibleAtSetPoint)
FBL.setClassTimed(elt, name, context, timeout);
else
{
delete elt.__invisibleAtSetPoint;
FBL.removeClass(elt, name);
}
}, timeout);
};
this.cancelClassTimed = function(elt, name, context)
{
if (elt.__setClassTimeout)
{
FBL.removeClass(elt, name);
context.clearTimeout(elt.__setClassTimeout);
delete elt.__setClassTimeout;
}
};
// ************************************************************************************************
// DOM queries
this.$ = function(id, doc)
{
if (doc)
return doc.getElementById(id);
else
return document.getElementById(id);
};
this.getChildByClass = function(node) // ,classname, classname, classname...
{
for (var i = 1; i < arguments.length; ++i)
{
var className = arguments[i];
var child = node.firstChild;
node = null;
for (; child; child = child.nextSibling)
{
if (this.hasClass(child, className))
{
node = child;
break;
}
}
}
return node;
};
this.getAncestorByClass = function(node, className)
{
for (var parent = node; parent; parent = parent.parentNode)
{
if (this.hasClass(parent, className))
return parent;
}
return null;
};
/* @Deprecated Use native Firefox: node.getElementsByClassName(names).item(0) */
this.getElementByClass = function(node, className) // className, className, ...
{
return FBL.getElementsByClass.apply(this,arguments).item(0);
};
/* @Deprecated Use native Firefox: node.getElementsByClassName(names) */
this.getElementsByClass = function(node, className) // className, className, ...
{
var args = cloneArray(arguments); args.splice(0, 1);
var className = args.join(" ");
return node.getElementsByClassName(className);
};
this.getElementsByAttribute = function(node, attrName, attrValue)
{
function iteratorHelper(node, attrName, attrValue, result)
{
for (var child = node.firstChild; child; child = child.nextSibling)
{
if (child.getAttribute(attrName) == attrValue)
result.push(child);
iteratorHelper(child, attrName, attrValue, result);
}
}
var result = [];
iteratorHelper(node, attrName, attrValue, result);
return result;
}
this.isAncestor = function(node, potentialAncestor)
{
for (var parent = node; parent; parent = parent.parentNode)
{
if (parent == potentialAncestor)
return true;
}
return false;
};
this.getNextElement = function(node)
{
while (node && node.nodeType != 1)
node = node.nextSibling;
return node;
};
this.getPreviousElement = function(node)
{
while (node && node.nodeType != 1)
node = node.previousSibling;
return node;
};
this.getBody = function(doc)
{
if (doc.body)
return doc.body;
var body = doc.getElementsByTagName("body")[0];
if (body)
return body;
return doc.documentElement; // For non-HTML docs
};
this.findNextDown = function(node, criteria)
{
if (!node)
return null;
for (var child = node.firstChild; child; child = child.nextSibling)
{
if (criteria(child))
return child;
var next = this.findNextDown(child, criteria);
if (next)
return next;
}
};
this.findPreviousUp = function(node, criteria)
{
if (!node)
return null;
for (var child = node.lastChild; child; child = child.previousSibling)
{
var next = this.findPreviousUp(child, criteria);
if (next)
return next;
if (criteria(child))
return child;
}
};
this.findNext = function(node, criteria, upOnly, maxRoot)
{
if (!node)
return null;
if (!upOnly)
{
var next = this.findNextDown(node, criteria);
if (next)
return next;
}
for (var sib = node.nextSibling; sib; sib = sib.nextSibling)
{
if (criteria(sib))
return sib;
var next = this.findNextDown(sib, criteria);
if (next)
return next;
}
if (node.parentNode && node.parentNode != maxRoot)
return this.findNext(node.parentNode, criteria, true);
};
this.findPrevious = function(node, criteria, downOnly, maxRoot)
{
if (!node)
return null;
for (var sib = node.previousSibling; sib; sib = sib.previousSibling)
{
var prev = this.findPreviousUp(sib, criteria);
if (prev)
return prev;
if (criteria(sib))
return sib;
}
if (!downOnly)
{
var next = this.findPreviousUp(node, criteria);
if (next)
return next;
}
if (node.parentNode && node.parentNode != maxRoot)
{
if (criteria(node.parentNode))
return node.parentNode;
return this.findPrevious(node.parentNode, criteria, true);
}
};
this.getNextByClass = function(root, state)
{
function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
return this.findNext(root, iter);
};
this.getPreviousByClass = function(root, state)
{
function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
return this.findPrevious(root, iter);
};
this.hasChildElements = function(node)
{
if (node.contentDocument) // iframes
return true;
for (var child = node.firstChild; child; child = child.nextSibling)
{
if (child.nodeType == 1)
return true;
}
return false;
};
this.isElement = function(o)
{
try {
return o && o instanceof Element;
}
catch (ex) {
return false;
}
};
this.isNode = function(o)
{
try {
return o && o instanceof Node;
}
catch (ex) {
return false;
}
};
this.XW_instanceof = function(obj, type) // Cross Window instanceof; type is local to this window
{
if (obj instanceof type)
return true; // within-window test
if (!type)
return false;
if (!obj)
return (type == "undefined");
// compare strings: obj constructor.name to type.name.
// This is not perfect, we should compare type.prototype to object.__proto__, but mostly code does not change the constructor object.
do
{
if (obj.constructor && obj.constructor.name == type.name) // then the function that constructed us is the argument
return true;
}
while(obj = obj.__proto__); // walk the prototype chain.
return false;
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Property_Inheritance_Revisited/Determining_Instance_Relationships
}
// ************************************************************************************************
// DOM Modification
this.setOuterHTML = function(element, html)
{
var doc = element.ownerDocument;
var range = doc.createRange();
range.selectNode(element || doc.documentElement);
try
{
var fragment = range.createContextualFragment(html);
var first = fragment.firstChild;
var last = fragment.lastChild;
element.parentNode.replaceChild(fragment, element);
return [first, last];
} catch (e)
{
return [element,element]
}
};
this.appendInnerHTML = function(element, html, referenceElement)
{
var doc = element.ownerDocument;
var range = doc.createRange(); // a helper object
range.selectNodeContents(element); // the environment to interpret the html
var fragment = range.createContextualFragment(html); // parse
var firstChild = fragment.firstChild;
element.insertBefore(fragment, referenceElement);
return firstChild;
};
this.insertTextIntoElement = function(element, text)
{
var command = "cmd_insertText";
var controller = element.controllers.getControllerForCommand(command);
if (!controller || !controller.isCommandEnabled(command))
return;
var params = this.CCIN("@mozilla.org/embedcomp/command-params;1", "nsICommandParams");
params.setStringValue("state_data", text);
controller = this.QI(controller, Ci.nsICommandController);
controller.doCommandWithParams(command, params);
};
// ************************************************************************************************
// XPath
/**
* Gets an XPath for an element which describes its hierarchical location.
*/
this.getElementXPath = function(element)
{
if (element && element.id)
return '//*[@id="' + element.id + '"]';
else
return this.getElementTreeXPath(element);
};
this.getElementTreeXPath = function(element)
{
var paths = [];
for (; element && element.nodeType == 1; element = element.parentNode)
{
var index = 0;
for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling)
{
if (sibling.localName == element.localName)
++index;
}
var tagName = element.localName.toLowerCase();
var pathIndex = (index ? "[" + (index+1) + "]" : "");
paths.splice(0, 0, tagName + pathIndex);
}
return paths.length ? "/" + paths.join("/") : null;
};
this.cssToXPath = function(rule)
{
var regElement = /^([#.]?)([a-z0-9\\*_-]*)((\|)([a-z0-9\\*_-]*))?/i;
var regAttr1 = /^\[([^\]]*)\]/i;
var regAttr2 = /^\[\s*([^~=\s]+)\s*(~?=)\s*"([^"]+)"\s*\]/i;
var regPseudo = /^:([a-z_-])+/i;
var regCombinator = /^(\s*[>+\s])?/i;
var regComma = /^\s*,/i;
var index = 1;
var parts = ["//", "*"];
var lastRule = null;
while (rule.length && rule != lastRule)
{
lastRule = rule;
// Trim leading whitespace
rule = this.trim(rule);
if (!rule.length)
break;
// Match the element identifier
var m = regElement.exec(rule);
if (m)
{
if (!m[1])
{
// XXXjoe Namespace ignored for now
if (m[5])
parts[index] = m[5];
else
parts[index] = m[2];
}
else if (m[1] == '#')
parts.push("[@id='" + m[2] + "']");
else if (m[1] == '.')
parts.push("[contains(@class, '" + m[2] + "')]");
rule = rule.substr(m[0].length);
}
// Match attribute selectors
m = regAttr2.exec(rule);
if (m)
{
if (m[2] == "~=")
parts.push("[contains(@" + m[1] + ", '" + m[3] + "')]");
else
parts.push("[@" + m[1] + "='" + m[3] + "']");
rule = rule.substr(m[0].length);
}
else
{
m = regAttr1.exec(rule);
if (m)
{
parts.push("[@" + m[1] + "]");
rule = rule.substr(m[0].length);
}
}
// Skip over pseudo-classes and pseudo-elements, which are of no use to us
m = regPseudo.exec(rule);
while (m)
{
rule = rule.substr(m[0].length);
m = regPseudo.exec(rule);
}
// Match combinators
m = regCombinator.exec(rule);
if (m && m[0].length)
{
if (m[0].indexOf(">") != -1)
parts.push("/");
else if (m[0].indexOf("+") != -1)
parts.push("/following-sibling::");
else
parts.push("//");
index = parts.length;
parts.push("*");
rule = rule.substr(m[0].length);
}
m = regComma.exec(rule);
if (m)
{
parts.push(" | ", "//", "*");
index = parts.length-1;
rule = rule.substr(m[0].length);
}
}
var xpath = parts.join("");
return xpath;
};
this.getElementsBySelector = function(doc, css)
{
var xpath = this.cssToXPath(css);
return this.getElementsByXPath(doc, xpath);
};
this.getElementsByXPath = function(doc, xpath)
{
var nodes = [];
try {
var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
for (var item = result.iterateNext(); item; item = result.iterateNext())
nodes.push(item);
}
catch (exc)
{
// Invalid xpath expressions make their way here sometimes. If that happens,
// we still want to return an empty set without an exception.
}
return nodes;
};
this.getRuleMatchingElements = function(rule, doc)
{
var css = rule.selectorText;
var xpath = this.cssToXPath(css);
return this.getElementsByXPath(doc, xpath);
};
// ************************************************************************************************
// Clipboard
this.copyToClipboard = function(string)
{
var clipboard = this.CCSV("@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
clipboard.copyString(string);
};
// ************************************************************************************************
// Graphics
this.getClientOffset = function(elt)
{
function addOffset(elt, coords, view)
{
var p = elt.offsetParent;
var style = view.getComputedStyle(elt, "");
if (elt.offsetLeft)
coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth);
if (elt.offsetTop)
coords.y += elt.offsetTop + parseInt(style.borderTopWidth);
if (p)
{
if (p.nodeType == 1)
addOffset(p, coords, view);
}
else if (elt.ownerDocument.defaultView.frameElement)
addOffset(elt.ownerDocument.defaultView.frameElement, coords, elt.ownerDocument.defaultView);
}
var coords = {x: 0, y: 0};
if (elt)
{
var view = elt.ownerDocument.defaultView;
addOffset(elt, coords, view);
}
return coords;
};
this.getLTRBWH = function(elt)
{
var bcrect,
dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0};
if (elt)
{
bcrect = elt.getBoundingClientRect();
dims.left = bcrect.left;
dims.top = bcrect.top;
dims.right = bcrect.right;
dims.bottom = bcrect.bottom;
if(bcrect.width)
{
dims.width = bcrect.width;
dims.height = bcrect.height;
}
else
{
dims.width = dims.right - dims.left;
dims.height = dims.bottom - dims.top;
}
}
return dims;
};
this.applyBodyOffsets = function(elt, clientRect)
{
var od = elt.ownerDocument;
if (!od.body)
return clientRect;
var style = od.defaultView.getComputedStyle(od.body, null);
var pos = style.getPropertyValue('position');
if(pos === 'absolute' || pos === 'relative')
{
var borderLeft = parseInt(style.getPropertyValue('border-left-width').replace('px', ''),10) || 0;
var borderTop = parseInt(style.getPropertyValue('border-top-width').replace('px', ''),10) || 0;
var paddingLeft = parseInt(style.getPropertyValue('padding-left').replace('px', ''),10) || 0;
var paddingTop = parseInt(style.getPropertyValue('padding-top').replace('px', ''),10) || 0;
var marginLeft = parseInt(style.getPropertyValue('margin-left').replace('px', ''),10) || 0;
var marginTop = parseInt(style.getPropertyValue('margin-top').replace('px', ''),10) || 0;
var offsetX = borderLeft + paddingLeft + marginLeft;
var offsetY = borderTop + paddingTop + marginTop;
clientRect.left -= offsetX;
clientRect.top -= offsetY;
clientRect.right -= offsetX;
clientRect.bottom -= offsetY;
}
return clientRect;
};
this.getOffsetSize = function(elt)
{
return {width: elt.offsetWidth, height: elt.offsetHeight};
};
this.getOverflowParent = function(element)
{
for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent)
{
if (scrollParent.scrollHeight > scrollParent.offsetHeight)
return scrollParent;
}
};
this.isScrolledToBottom = function(element)
{
var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight;
return onBottom;
};
this.scrollToBottom = function(element)
{
element.scrollTop = element.scrollHeight;
return (element.scrollTop == element.scrollHeight);
};
this.move = function(element, x, y)
{
element.style.left = x + "px";
element.style.top = y + "px";
};
this.resize = function(element, w, h)
{
element.style.width = w + "px";
element.style.height = h + "px";
};
this.linesIntoCenterView = function(element, scrollBox) // {before: int, after: int}
{
if (!scrollBox)
scrollBox = this.getOverflowParent(element);
if (!scrollBox)
return;
var offset = this.getClientOffset(element);
var topSpace = offset.y - scrollBox.scrollTop;
var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
- (offset.y + element.offsetHeight);
if (topSpace < 0 || bottomSpace < 0)
{
var split = (scrollBox.clientHeight/2);
var centerY = offset.y - split;
scrollBox.scrollTop = centerY;
topSpace = split;
bottomSpace = split - element.offsetHeight;
}
return {before: Math.round((topSpace/element.offsetHeight) + 0.5),
after: Math.round((bottomSpace/element.offsetHeight) + 0.5) }
};
this.scrollIntoCenterView = function(element, scrollBox, notX, notY)
{
if (!element)
return;
if (!scrollBox)
scrollBox = this.getOverflowParent(element);
if (!scrollBox)
return;
var offset = this.getClientOffset(element);
if (!notY)
{
var topSpace = offset.y - scrollBox.scrollTop;
var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
- (offset.y + element.offsetHeight);
if (topSpace < 0 || bottomSpace < 0)
{
var centerY = offset.y - (scrollBox.clientHeight/2);
scrollBox.scrollTop = centerY;
}
}
if (!notX)
{
var leftSpace = offset.x - scrollBox.scrollLeft;
var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth)
- (offset.x + element.clientWidth);
if (leftSpace < 0 || rightSpace < 0)
{
var centerX = offset.x - (scrollBox.clientWidth/2);
scrollBox.scrollLeft = centerX;
}
}
};
// ************************************************************************************************
// CSS
var cssKeywordMap = null;
var cssPropNames = null;
var cssColorNames = null;
var imageRules = null;
this.getCSSKeywordsByProperty = function(propName)
{
if (!cssKeywordMap)
{
cssKeywordMap = {};
for (var name in this.cssInfo)
{
var list = [];
var types = this.cssInfo[name];
for (var i = 0; i < types.length; ++i)
{
var keywords = this.cssKeywords[types[i]];
if (keywords)
list.push.apply(list, keywords);
}
cssKeywordMap[name] = list;
}
}
return propName in cssKeywordMap ? cssKeywordMap[propName] : [];
};
this.getCSSPropertyNames = function()
{
if (!cssPropNames)
{
cssPropNames = [];
for (var name in this.cssInfo)
cssPropNames.push(name);
}
return cssPropNames;
};
this.isColorKeyword = function(keyword)
{
if (keyword == "transparent")
return false;
if (!cssColorNames)
{
cssColorNames = [];
var colors = this.cssKeywords["color"];
for (var i = 0; i < colors.length; ++i)
cssColorNames.push(colors[i].toLowerCase());
var systemColors = this.cssKeywords["systemColor"];
for (var i = 0; i < systemColors.length; ++i)
cssColorNames.push(systemColors[i].toLowerCase());
}
return cssColorNames.indexOf(keyword.toLowerCase()) != -1;
};
this.isImageRule = function(rule)
{
if (!imageRules)
{
imageRules = [];
for (var i in this.cssInfo)
{
var r = i.toLowerCase();
var suffix = "image";
if (r.match(suffix + "$") == suffix || r == "background")
imageRules.push(r);
}
}
return imageRules.indexOf(rule.toLowerCase()) != -1;
};
this.copyTextStyles = function(fromNode, toNode, style)
{
var view = fromNode.ownerDocument.defaultView;
if (view)
{
if (!style)
style = view.getComputedStyle(fromNode, "");
toNode.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
toNode.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
toNode.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
toNode.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
return style;
}
};
this.copyBoxStyles = function(fromNode, toNode, style)
{
var view = fromNode.ownerDocument.defaultView;
if (view)
{
if (!style)
style = view.getComputedStyle(fromNode, "");
toNode.style.marginTop = style.getPropertyCSSValue("margin-top").cssText;
toNode.style.marginRight = style.getPropertyCSSValue("margin-right").cssText;
toNode.style.marginBottom = style.getPropertyCSSValue("margin-bottom").cssText;
toNode.style.marginLeft = style.getPropertyCSSValue("margin-left").cssText;
toNode.style.borderTopWidth = style.getPropertyCSSValue("border-top-width").cssText;
toNode.style.borderRightWidth = style.getPropertyCSSValue("border-right-width").cssText;
toNode.style.borderBottomWidth = style.getPropertyCSSValue("border-bottom-width").cssText;
toNode.style.borderLeftWidth = style.getPropertyCSSValue("border-left-width").cssText;
return style;
}
};
this.readBoxStyles = function(style)
{
const styleNames = {
"margin-top": "marginTop", "margin-right": "marginRight",
"margin-left": "marginLeft", "margin-bottom": "marginBottom",
"border-top-width": "borderTop", "border-right-width": "borderRight",
"border-left-width": "borderLeft", "border-bottom-width": "borderBottom",
"padding-top": "paddingTop", "padding-right": "paddingRight",
"padding-left": "paddingLeft", "padding-bottom": "paddingBottom",
"z-index": "zIndex",
};
var styles = {};
for (var styleName in styleNames)
styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText) || 0;
return styles;
};
this.getBoxFromStyles = function(style, element)
{
var args = this.readBoxStyles(style);
args.width = element.offsetWidth
- (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight);
args.height = element.offsetHeight
- (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom);
return args;
};
this.getElementCSSSelector = function(element)
{
var label = element.localName.toLowerCase();
if (element.id)
label += "#" + element.id;
if (element.hasAttribute("class"))
label += "." + element.getAttribute("class").split(" ")[0];
return label;
};
this.getURLForStyleSheet= function(styleSheet)
{
//http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null.
return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL);
};
this.getDocumentForStyleSheet = function(styleSheet)
{
while (styleSheet.parentStyleSheet && !styleSheet.ownerNode)
{
styleSheet = styleSheet.parentStyleSheet;
}
if (styleSheet.ownerNode)
return styleSheet.ownerNode.ownerDocument;
};
/**
* Retrieves the instance number for a given style sheet. The instance number
* is sheet's index within the set of all other sheets whose URL is the same.
*/
this.getInstanceForStyleSheet = function(styleSheet, ownerDocument)
{
// System URLs are always unique (or at least we are making this assumption)
if (FBL.isSystemStyleSheet(styleSheet))
return 0;
// ownerDocument is an optional hint for performance
ownerDocument = ownerDocument || FBL.getDocumentForStyleSheet(styleSheet);
var ret = 0,
styleSheets = ownerDocument.styleSheets,
href = styleSheet.href;
for (var i = 0; i < styleSheets.length; i++)
{
var curSheet = styleSheets[i];
if (curSheet == styleSheet)
break;
if (curSheet.href == href)
ret++;
}
return ret;
};
// ************************************************************************************************
// HTML and XML Serialization
var getElementType = this.getElementType = function(node)
{
if (isElementXUL(node))
return 'xul';
else if (isElementSVG(node))
return 'svg';
else if (isElementMathML(node))
return 'mathml';
else if (isElementXHTML(node))
return 'xhtml';
else if (isElementHTML(node))
return 'html';
}
var isElementHTML = this.isElementHTML = function(node)
{
return node.nodeName == node.nodeName.toUpperCase();
}
var isElementXHTML = this.isElementXHTML = function(node)
{
return node.nodeName == node.nodeName.toLowerCase();
}
var isElementMathML = this.isElementMathML = function(node)
{
return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
}
var isElementSVG = this.isElementSVG = function(node)
{
return node.namespaceURI == 'http://www.w3.org/2000/svg';
}
var isElementXUL = this.isElementXUL = function(node)
{
return node instanceof XULElement;
}
this.isSelfClosing = function(element)
{
if (isElementSVG(element) || isElementMathML(element))
return true;
var tag = element.localName.toLowerCase();
return (this.selfClosingTags.hasOwnProperty(tag));
};
this.getElementHTML = function(element)
{
var self=this;
function toHTML(elt)
{
if (elt.nodeType == Node.ELEMENT_NODE)
{
if (unwrapObject(elt).firebugIgnore)
return;
html.push('<', elt.nodeName.toLowerCase());
for (var i = 0; i < elt.attributes.length; ++i)
{
var attr = elt.attributes[i];
// Hide attributes set by Firebug
if (attr.localName.indexOf("firebug-") == 0)
continue;
// MathML
if (attr.localName.indexOf("-moz-math") == 0)
{
// just hide for now
continue;
}
html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
}
if (elt.firstChild)
{
html.push('>');
var pureText=true;
for (var child = element.firstChild; child; child = child.nextSibling)
pureText=pureText && (child.nodeType == Node.TEXT_NODE);
if (pureText)
html.push(escapeForHtmlEditor(elt.textContent));
else {
for (var child = elt.firstChild; child; child = child.nextSibling)
toHTML(child);
}
html.push('</', elt.nodeName.toLowerCase(), '>');
}
else if (isElementSVG(elt) || isElementMathML(elt))
{
html.push('/>');
}
else if (self.isSelfClosing(elt))
{
html.push((isElementXHTML(elt))?'/>':'>');
}
else
{
html.push('></', elt.nodeName.toLowerCase(), '>');
}
}
else if (elt.nodeType == Node.TEXT_NODE)
html.push(escapeForTextNode(elt.textContent));
else if (elt.nodeType == Node.CDATA_SECTION_NODE)
html.push('<![CDATA[', elt.nodeValue, ']]>');
else if (elt.nodeType == Node.COMMENT_NODE)
html.push('<!--', elt.nodeValue, '-->');
}
var html = [];
toHTML(element);
return html.join("");
};
this.getElementXML = function(element)
{
function toXML(elt)
{
if (elt.nodeType == Node.ELEMENT_NODE)
{
if (unwrapObject(elt).firebugIgnore)
return;
xml.push('<', elt.nodeName.toLowerCase());
for (var i = 0; i < elt.attributes.length; ++i)
{
var attr = elt.attributes[i];
// Hide attributes set by Firebug
if (attr.localName.indexOf("firebug-") == 0)
continue;
// MathML
if (attr.localName.indexOf("-moz-math") == 0)
{
// just hide for now
continue;
}
xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
}
if (elt.firstChild)
{
xml.push('>');
for (var child = elt.firstChild; child; child = child.nextSibling)
toXML(child);
xml.push('</', elt.nodeName.toLowerCase(), '>');
}
else
xml.push('/>');
}
else if (elt.nodeType == Node.TEXT_NODE)
xml.push(elt.nodeValue);
else if (elt.nodeType == Node.CDATA_SECTION_NODE)
xml.push('<![CDATA[', elt.nodeValue, ']]>');
else if (elt.nodeType == Node.COMMENT_NODE)
xml.push('<!--', elt.nodeValue, '-->');
}
var xml = [];
toXML(element);
return xml.join("");
};
// ************************************************************************************************
// Whitespace and Entity conversions
var entityConversionLists = this.entityConversionLists = {
normal : {
whitespace : {
'\t' : '\u200c\u2192',
'\n' : '\u200c\u00b6',
'\r' : '\u200c\u00ac',
' ' : '\u200c\u00b7'
}
},
reverse : {
whitespace : {
'	' : '\t',
'
' : '\n',
'\u200c\u2192' : '\t',
'\u200c\u00b6' : '\n',
'\u200c\u00ac' : '\r',
'\u200c\u00b7' : ' '
}
}
};
var normal = entityConversionLists.normal,
reverse = entityConversionLists.reverse;
function addEntityMapToList(ccode, entity)
{
var lists = Array.slice(arguments, 2),
len = lists.length,
ch = String.fromCharCode(ccode);
for (var i = 0; i < len; i++)
{
var list = lists[i];
normal[list]=normal[list] || {};
normal[list][ch] = '&' + entity + ';';
reverse[list]=reverse[list] || {};
reverse[list]['&' + entity + ';'] = ch;
}
}
var e = addEntityMapToList,
white = 'whitespace',
text = 'text',
attr = 'attributes',
css = 'css',
editor = 'editor';
e(0x0022, 'quot', attr, css);
e(0x0026, 'amp', attr, text, css);
e(0x0027, 'apos', css);
e(0x003c, 'lt', attr, text, css);
e(0x003e, 'gt', attr, text, css);
e(0xa9, 'copy', text, editor);
e(0xae, 'reg', text, editor);
e(0x2122, 'trade', text, editor);
// See http://en.wikipedia.org/wiki/Dash
e(0x2012, '#8210', attr, text, editor); // figure dash
e(0x2013, 'ndash', attr, text, editor); // en dash
e(0x2014, 'mdash', attr, text, editor); // em dash
e(0x2015, '#8213', attr, text, editor); // horizontal bar
e(0x00a0, 'nbsp', attr, text, white, editor);
e(0x2002, 'ensp', attr, text, white, editor);
e(0x2003, 'emsp', attr, text, white, editor);
e(0x2009, 'thinsp', attr, text, white, editor);
e(0x200c, 'zwnj', attr, text, white, editor);
e(0x200d, 'zwj', attr, text, white, editor);
e(0x200e, 'lrm', attr, text, white, editor);
e(0x200f, 'rlm', attr, text, white, editor);
e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP)
//************************************************************************************************
// Entity escaping
var entityConversionRegexes = {
normal : {},
reverse : {}
};
var escapeEntitiesRegEx = {
normal : function(list)
{
var chars = [];
for ( var ch in list)
{
chars.push(ch);
}
return new RegExp('([' + chars.join('') + '])', 'gm');
},
reverse : function(list)
{
var chars = [];
for ( var ch in list)
{
chars.push(ch);
}
return new RegExp('(' + chars.join('|') + ')', 'gm');
}
};
function getEscapeRegexp(direction, lists)
{
var name = '', re;
var groups = [].concat(lists);
for (i = 0; i < groups.length; i++)
{
name += groups[i].group;
}
re = entityConversionRegexes[direction][name];
if (!re)
{
var list = {};
if (groups.length > 1)
{
for ( var i = 0; i < groups.length; i++)
{
var aList = entityConversionLists[direction][groups[i].group];
for ( var item in aList)
list[item] = aList[item];
}
} else if (groups.length==1)
{
list = entityConversionLists[direction][groups[0].group]; // faster for special case
} else {
list = {}; // perhaps should print out an error here?
}
re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list);
}
return re;
}
function createSimpleEscape(name, direction)
{
return function(value)
{
var list = entityConversionLists[direction][name];
return String(value).replace(
getEscapeRegexp(direction, {
group : name,
list : list
}),
function(ch)
{
return list[ch];
}
);
}
}
function escapeGroupsForEntities(str, lists)
{
lists = [].concat(lists);
var re = getEscapeRegexp('normal', lists),
split = String(str).split(re),
len = split.length,
results = [],
cur, r, i, ri = 0, l, list, last = '';
if (!len)
return [ {
str : String(str),
group : '',
name : ''
} ];
for (i = 0; i < len; i++)
{
cur = split[i];
if (cur == '')
continue;
for (l = 0; l < lists.length; l++)
{
list = lists[l];
r = entityConversionLists.normal[list.group][cur];
// if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space
// r = ' ';
if (r)
{
results[ri] = {
'str' : r,
'class' : list['class'],
'extra' : list.extra[cur] ? list['class']
+ list.extra[cur] : ''
};
break;
}
}
// last=cur;
if (!r)
results[ri] = {
'str' : cur,
'class' : '',
'extra' : ''
};
ri++;
}
return results;
}
this.escapeGroupsForEntities = escapeGroupsForEntities;
function unescapeEntities(str, lists)
{
var re = getEscapeRegexp('reverse', lists),
split = String(str).split(re),
len = split.length,
results = [],
cur, r, i, ri = 0, l, list;
if (!len)
return str;
lists = [].concat(lists);
for (i = 0; i < len; i++)
{
cur = split[i];
if (cur == '')
continue;
for (l = 0; l < lists.length; l++)
{
list = lists[l];
r = entityConversionLists.reverse[list.group][cur];
if (r)
{
results[ri] = r;
break;
}
}
if (!r)
results[ri] = cur;
ri++;
}
return results.join('') || '';
}
// ************************************************************************************************
// String escaping
var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal');
var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal');
var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal');
var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal');
// deprecated compatibility functions
this.deprecateEscapeHTML = createSimpleEscape('text', 'normal');
this.deprecatedUnescapeHTML = createSimpleEscape('text', 'reverse');
this.escapeHTML = deprecated("use appropriate escapeFor... function", this.deprecateEscapeHTML);
this.unescapeHTML = deprecated("use appropriate unescapeFor... function", this.deprecatedUnescapeHTML);
var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal');
var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse');
this.unescapeForTextNode = function(str)
{
if (Firebug.showTextNodesWithWhitespace)
str = unescapeWhitespace(str);
if (!Firebug.showTextNodesWithEntities)
str = escapeForElementAttribute(str);
return str;
}
this.escapeNewLines = function(value)
{
return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
};
this.stripNewLines = function(value)
{
return typeof(value) == "string" ? value.replace(/[\r\n]/gm, " ") : value;
};
this.escapeJS = function(value)
{
return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
};
this.cropString = function(text, limit, alterText)
{
if (!alterText)
alterText = "..."; //…
text = text + "";
if (!limit)
limit = Firebug.stringCropLength;
var halfLimit = (limit / 2);
halfLimit -= 2; // adjustment for alterText's increase in size
if (text.length > limit)
return text.substr(0, halfLimit) + alterText + text.substr(text.length-halfLimit);
else
return text;
};
this.cropMultipleLines = function(text, limit)
{
return this.escapeNewLines(this.cropString(text, limit));
};
this.isWhitespace = function(text)
{
return !reNotWhitespace.exec(text);
};
this.splitLines = function(text)
{
const reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg;
var lines;
if (text.match)
{
lines = text.match(reSplitLines2);
}
else
{
var str = text+"";
lines = str.match(reSplitLines2);
}
lines.pop();
return lines;
};
this.trim = function(text)
{
return text.replace(/^\s*|\s*$/g,"");
}
this.trimLeft = function(text)
{
return text.replace(/^\s+/,"");
}
this.trimRight = function(text)
{
return text.replace(/\s+$/,"");
}
this.wrapText = function(text, noEscapeHTML)
{
var reNonAlphaNumeric = /[^A-Za-z_$0-9'"-]/;
var html = [];
var wrapWidth = Firebug.textWrapWidth;
// Split long text into lines and put every line into an <code> element (only in case
// if noEscapeHTML is false). This is useful for automatic scrolling when searching
// within response body (in order to scroll we need an element).
// Don't use <pre> elements since these adds addiontanl new line ending when copying
// selected source code using Firefox->Edit->Copy (Ctrl+C) (issue 2093).
var lines = this.splitLines(text);
for (var i = 0; i < lines.length; ++i)
{
var line = lines[i];
while (line.length > wrapWidth)
{
var m = reNonAlphaNumeric.exec(line.substr(wrapWidth, 100));
var wrapIndex = wrapWidth + (m ? m.index : 0);
var subLine = line.substr(0, wrapIndex);
line = line.substr(wrapIndex);
if (!noEscapeHTML) html.push("<code class=\"wrappedText focusRow\" role=\"listitem\">");
html.push(noEscapeHTML ? subLine : escapeForTextNode(subLine));
if (!noEscapeHTML) html.push("</code>");
}
if (!noEscapeHTML) html.push("<code class=\"wrappedText focusRow\" role=\"listitem\">");
html.push(noEscapeHTML ? line : escapeForTextNode(line));
if (!noEscapeHTML) html.push("</code>");
}
return html;
}
this.insertWrappedText = function(text, textBox, noEscapeHTML)
{
var html = this.wrapText(text, noEscapeHTML);
textBox.innerHTML = "<pre role=\"list\">" + html.join("") + "</pre>";
}
// ************************************************************************************************
// Menus
this.createMenu = function(popup, label)
{
var menu = popup.ownerDocument.createElement("menu");
menu.setAttribute("label", label);
var menuPopup = popup.ownerDocument.createElement("menupopup");
popup.appendChild(menu);
menu.appendChild(menuPopup);
return menuPopup;
};
this.createMenuItem = function(popup, item, before)
{
if (typeof(item) == "string" && item.indexOf("-") == 0)
return this.createMenuSeparator(popup, before);
var menuitem = popup.ownerDocument.createElement("menuitem");
this.setItemIntoElement(menuitem, item);
if (before)
popup.insertBefore(menuitem, before);
else
popup.appendChild(menuitem);
return menuitem;
};
this.setItemIntoElement = function(element, item)
{
var label = item.nol10n ? item.label : this.$STR(item.label);
element.setAttribute("label", label);
element.setAttribute("type", item.type);
if (item.checked)
element.setAttribute("checked", "true");
if (item.disabled)
element.setAttribute("disabled", "true");
if (item.image)
{
element.setAttribute("class", "element-iconic");
element.setAttribute("image", item.image);
}
if (item.command)
element.addEventListener("command", item.command, false);
if (item.commandID)
element.setAttribute("command", item.commandID);
if (item.option)
element.setAttribute("option", item.option);
if (item.tooltiptext)
element.setAttribute("tooltiptext", item.tooltiptext);
return element;
}
this.createMenuHeader = function(popup, item)
{
var header = popup.ownerDocument.createElement("label");
header.setAttribute("class", "menuHeader");
var label = item.nol10n ? item.label : this.$STR(item.label);
header.setAttribute("value", label);
popup.appendChild(header);
return header;
};
this.createMenuSeparator = function(popup, before)
{
if (!popup.firstChild)
return;
var menuitem = popup.ownerDocument.createElement("menuseparator");
if (before)
popup.insertBefore(menuitem, before);
else
popup.appendChild(menuitem);
return menuitem;
};
this.optionMenu = function(label, option)
{
return {label: label, type: "checkbox", checked: Firebug[option], option: option,
command: this.bindFixed(Firebug.setPref, Firebug, Firebug.prefDomain, option, !Firebug[option]) };
};
this.serviceOptionMenu = function(label, option)
{
return {label: label, type: "checkbox", checked: Firebug[option], option: option,
command: this.bindFixed(Firebug.setPref, Firebug, Firebug.servicePrefDomain, option, !Firebug[option]) };
};
// ************************************************************************************************
// Stack Traces
this.getCurrentStackTrace = function(context)
{
var trace = null;
Firebug.Debugger.halt(function(frame)
{
trace = FBL.getCorrectedStackTrace(frame, context);
});
return trace;
};
this.getCurrentJSDStackDump = function()
{
var trace = null;
Firebug.Debugger.halt(function(frame)
{
trace = FBL.getJSDStackDump(frame);
});
return trace;
};
this.getStackTrace = deprecated("name change for self-documentation", this.getCorrectedStackTrace);
this.getCorrectedStackTrace = function(frame, context)
{
var trace = new this.StackTrace();
for (; frame && frame.isValid; frame = frame.callingFrame)
{
if (!(Firebug.filterSystemURLs && this.isSystemURL(FBL.normalizeURL(frame.script.fileName))))
{
var stackFrame = this.getStackFrame(frame, context);
if (stackFrame)
trace.frames.push(stackFrame);
}
else
{
}
}
if (trace.frames.length > 100)
{
var originalLength = trace.frames.length;
trace.frames.splice(50, originalLength - 100);
var excuse = "(eliding "+(originalLength - 100)+" frames)";
trace.frames[50] = new this.StackFrame(context, excuse, null, excuse, 0, []);
}
return trace;
};
this.getStackFrame = function(frame, context)
{
if (frame.isNative || frame.isDebugger)
{
var excuse = (frame.isNative) ? "(native)" : "(debugger)";
return new this.StackFrame(context, excuse, null, excuse, 0, []);
}
try
{
var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, frame.script);
if (sourceFile)
{
var url = sourceFile.href;
var analyzer = sourceFile.getScriptAnalyzer(frame.script);
var lineNo = analyzer.getSourceLineFromFrame(context, frame);
var fncSpec = analyzer.getFunctionDescription(frame.script, context, frame);
if (!fncSpec.name)
fncSpec.name = frame.script.functionName;
return new this.StackFrame(context, fncSpec.name, frame.script, url, lineNo, fncSpec.args, frame.pc);
}
else
{
var script = frame.script;
return new this.StackFrame(context, script.functionName, frame.script, FBL.normalizeURL(script.fileName), frame.line, [], frame.pc);
}
}
catch (exc)
{
return null;
}
};
function getStackDump()
{
var lines = [];
for (var frame = Components.stack; frame; frame = frame.caller)
lines.push(frame.filename + " (" + frame.lineNumber + ")");
return lines.join("\n");
};
this.getStackDump = getStackDump;
this.getJSDStackDump = function(newestFrame)
{
var lines = [];
for (var frame = newestFrame; frame; frame = frame.callingFrame)
lines.push(frame.script.fileName + " (" + frame.line + ")");
return lines.join("\n");
};
this.getStackSourceLink = function()
{
for (var frame = Components.stack; frame; frame = frame.caller)
{
if (frame.filename && frame.filename.indexOf("chrome://firebug/") == 0)
{
for (; frame; frame = frame.caller)
{
var firebugComponent = "/components/firebug-";
if (frame.filename && frame.filename.indexOf("chrome://firebug/") != 0 &&
frame.filename.indexOf(firebugComponent) == -1)
break;
}
break;
}
}
return this.getFrameSourceLink(frame);
}
this.getFrameSourceLink = function(frame)
{
if (frame && frame.filename && frame.filename.indexOf("XPCSafeJSObjectWrapper") == -1)
return new FBL.SourceLink(frame.filename, frame.lineNumber, "js");
else
return null;
};
this.getStackFrameId = function()
{
for (var frame = Components.stack; frame; frame = frame.caller)
{
if (frame.languageName == "JavaScript"
&& !(frame.filename && frame.filename.indexOf("chrome://firebug/") == 0))
{
return frame.filename + "/" + frame.lineNumber;
}
}
return null;
};
// ************************************************************************************************
// Event Monitoring
this.toggleMonitorEvents = function(object, type, state, context)
{
if (state)
this.unmonitorEvents(object, type, context);
else
this.monitorEvents(object, type, context);
};
this.monitorEvents = function(object, type, context)
{
if (!this.areEventsMonitored(object, type, context) && object && object.addEventListener)
{
if (!context.onMonitorEvent)
context.onMonitorEvent = function(event) { Firebug.Console.log(event, context); };
if (!context.eventsMonitored)
context.eventsMonitored = [];
context.eventsMonitored.push({object: object, type: type});
if (!type)
this.attachAllListeners(object, context.onMonitorEvent, context);
else
object.addEventListener(type, context.onMonitorEvent, false);
}
};
this.unmonitorEvents = function(object, type, context)
{
var eventsMonitored = context.eventsMonitored;
for (var i = 0; i < eventsMonitored.length; ++i)
{
if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
{
eventsMonitored.splice(i, 1);
if (!type)
this.detachAllListeners(object, context.onMonitorEvent, context);
else
object.removeEventListener(type, context.onMonitorEvent, false);
break;
}
}
};
this.areEventsMonitored = function(object, type, context)
{
var eventsMonitored = context.eventsMonitored;
if (eventsMonitored)
{
for (var i = 0; i < eventsMonitored.length; ++i)
{
if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
return true;
}
}
return false;
};
// ************************************************************************************************
// Functions
this.findScripts = function(context, url, line)
{
var sourceFile = context.sourceFileMap[url];
if (sourceFile)
var scripts = sourceFile.scriptsIfLineCouldBeExecutable(line);
else
{
}
return scripts;
};
this.findScriptForFunctionInContext = function(context, fn)
{
var found = null;
if (!fn || !fn.toString)
return found;
var fns = fn.toSource();
var found = this.forEachFunction(context, function findMatchingScript(script, aFunction)
{
if (!aFunction['toSource'] || typeof(aFunction['toSource']) != "function")
return;
try {
var tfs = aFunction.toSource();
} catch (etfs) {
FBTrace.sysout("unwrapped.toSource fails for unwrapped: "+etfs, aFunction);
}
if (tfs == fns)
return script;
});
return found;
}
this.forEachFunction = function(context, cb)
{
for (var url in context.sourceFileMap)
{
var sourceFile = context.sourceFileMap[url];
var rc = sourceFile.forEachScript(function seekFn(script, sourceFile)
{
if (!script.isValid)
return;
try
{
var testFunctionObject = script.functionObject; // Boris says this object is bogus.
if (!testFunctionObject.isValid)
return false;
var theFunction = FBL.unwrapIValue(testFunctionObject);
if (theFunction)
{
var rc = cb(script, theFunction, sourceFile);
if (rc)
return rc;
}
}
catch(exc)
{
}
});
if (rc)
return rc;
}
return false;
}
this.findScriptForFunction = function(fn)
{
var found = {tag: "not set"};
this.jsd.enumerateScripts({enumerateScript: function findScriptMatchingFn(script)
{
try {
if (script.isValid)
{
var iValueFunctionObject = script.functionObject;
//FBTrace.dumpIValue("lib.findScriptForFunction iValueFunctionObject", iValueFunctionObject);
var testFunctionObject = FBL.unwrapIValue(script.functionObject);
if (testFunctionObject instanceof Function)
FBTrace.sysout("lib.findScriptForFunction testFunctionObject "+testFunctionObject+" vs "+fn+"\n");
if (testFunctionObject == fn)
{
found = script;
return;
}
}
} catch (exc) {
}
}});
FBTrace.sysout("findScriptForFunction found ", found.tag);
return found;
};
this.findSourceForFunction = function(fn, context)
{
var script = this.findScriptForFunctionInContext(context, fn);
return (script)? this.getSourceLinkForScript(script, context) : null;
};
this.getSourceLinkForScript = function(script, context)
{
var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script);
if (sourceFile)
{
var scriptAnalyzer = sourceFile.getScriptAnalyzer(script);
if (scriptAnalyzer)
return scriptAnalyzer.getSourceLinkForScript(script);
else
{
// no-op for detrace
}
}
};
this.getFunctionName = function(script, context, frame, noArgs)
{
if (!script)
{
return "(no script)";
}
var name = script.functionName;
if (!name || (name == "anonymous"))
{
var analyzer = Firebug.SourceFile.getScriptAnalyzer(context, script);
if (analyzer)
{
var functionSpec = analyzer.getFunctionDescription(script, context, frame);
name = functionSpec.name + (noArgs ? "" : "("+functionSpec.args.join(',')+")");
}
else
{
name = this.guessFunctionName(FBL.normalizeURL(script.fileName), script.baseLineNumber, context);
}
}
return name;
};
this.guessFunctionName = function(url, lineNo, context)
{
if (context)
{
if (context.sourceCache)
return this.guessFunctionNameFromLines(url, lineNo, context.sourceCache);
}
return "? in "+this.getFileName(url)+"@"+lineNo;
};
this.guessFunctionNameFromLines = function(url, lineNo, sourceCache)
{
// Walk backwards from the first line in the function until we find the line which
// matches the pattern above, which is the function definition
var line = "";
for (var i = 0; i < 4; ++i)
{
line = sourceCache.getLine(url, lineNo-i) + line;
if (line != undefined)
{
var m = reGuessFunction.exec(line);
if (m)
return m[1];
else
{
}
m = reFunctionArgNames.exec(line);
if (m && m[1])
return m[1];
}
}
return "(?)";
};
this.getFunctionArgNames = function(frame)
{
var script = frame.script;
if (script.getParameterNames) // FF 3.6 or later
{
return script.getParameterNames();
}
else // FF 3.5 or eariler
{
return []; // TODO
var m = reFunctionArgNames.exec(this.safeToString(fn));
if (m)
{
var argNames = m[2].split(", ");
if (argNames.length && argNames[0])
return argNames;
}
return [];
}
};
this.getFunctionArgValues = function(frame)
{
var values = [];
var argNames = this.getFunctionArgNames(frame);
var scope = FBL.unwrapIValue(frame.scope);
for (var i = 0; i < argNames.length; ++i)
{
var argName = argNames[i];
if (scope)
{
var pvalue = scope.argName;
var value = pvalue ? FBL.unwrapIValue(pvalue.value) : undefined;
values.push({name: argName, value: value});
}
else
{
values.push({name: argName});
}
}
return values;
};
// ************************************************************************************************
// Source Files
this.getSourceFileByHref = function(url, context)
{
return context.sourceFileMap[url];
};
this.getAllStyleSheets = function(context)
{
var styleSheets = [];
function addSheet(sheet)
{
var sheetLocation = FBL.getURLForStyleSheet(sheet);
if (!Firebug.showUserAgentCSS && FBL.isSystemURL(sheetLocation))
return;
styleSheets.push(sheet);
try
{
for (var i = 0; i < sheet.cssRules.length; ++i)
{
var rule = sheet.cssRules[i];
if (rule instanceof CSSImportRule)
addSheet(rule.styleSheet);
}
}
catch(e)
{
}
}
this.iterateWindows(context.window, function(subwin)
{
var rootSheets = subwin.document.styleSheets;
for (var i = 0; i < rootSheets.length; ++i)
addSheet(rootSheets[i]);
});
return styleSheets;
};
this.getStyleSheetByHref = function(url, context)
{
if (!context.styleSheetMap)
FBL.createStyleSheetMap(context); // fill cache
return context.styleSheetMap.hasOwnProperty(url) ? context.styleSheetMap[url] : undefined;
};
this.createStyleSheetMap = function(context)
{
context.styleSheetMap = {};
function addSheet(sheet)
{
var sheetURL = FBL.getURLForStyleSheet(sheet);
context.styleSheetMap[sheetURL] = sheet;
for (var i = 0; i < sheet.cssRules.length; ++i)
{
var rule = sheet.cssRules[i];
if (rule instanceof CSSStyleRule)
{
if (rule.type == CSSRule.STYLE_RULE) // once we get here no more imports
return;
}
else if (rule instanceof CSSImportRule)
{
addSheet(rule.styleSheet);
}
}
}
this.iterateWindows(context.window, function(subwin)
{
var rootSheets = subwin.document.styleSheets;
for (var i = 0; i < rootSheets.length; ++i)
{
addSheet(rootSheets[i]);
}
});
return context.styleSheetMap;
};
this.sourceURLsAsArray = function(context)
{
var urls = [];
var sourceFileMap = context.sourceFileMap;
for (var url in sourceFileMap)
urls.push(url);
return urls;
};
this.sourceFilesAsArray = function(sourceFileMap)
{
var sourceFiles = [];
for (var url in sourceFileMap)
sourceFiles.push(sourceFileMap[url]);
return sourceFiles;
};
// ************************************************************************************************
// Firefox browsing
this.openNewTab = function(url, postText)
{
if (!url)
return;
var postData = null;
if (postText)
{
var stringStream = this.getInputStreamFromString(postText);
postData = this.CCIN("@mozilla.org/network/mime-input-stream;1", "nsIMIMEInputStream");
postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
postData.addContentLength = true;
postData.setData(stringStream);
}
gBrowser.selectedTab = gBrowser.addTab(url, null, null, postData);
};
this.openWindow = function(windowType, url, features, params)
{
var win = windowType ? wm.getMostRecentWindow(windowType) : null;
if (win) {
if ("initWithParams" in win)
win.initWithParams(params);
win.focus();
}
else {
var winFeatures = "resizable,dialog=no,centerscreen" + (features != "" ? ("," + features) : "");
var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener;
win = parentWindow.openDialog(url, "_blank", winFeatures, params);
}
return win;
};
this.viewSource = function(url, lineNo)
{
window.openDialog("chrome://global/content/viewSource.xul", "_blank",
"all,dialog=no", url, null, null, lineNo);
};
// Iterate over all opened firefox windows of the given type. If the callback returns true
// the iteration is stopped.
this.iterateBrowserWindows = function(windowType, callback)
{
var windowList = wm.getZOrderDOMWindowEnumerator(windowType, true);
if (!windowList.hasMoreElements())
windowList = wm.getEnumerator(windowType);
while (windowList.hasMoreElements()) {
if (callback(windowList.getNext()))
return true;
}
return false;
};
this.iterateBrowserTabs = function(browserWindow, callback)
{
var tabBrowser = browserWindow.getBrowser();
var numTabs = tabBrowser.browsers.length;
for(var index=0; index<numTabs; index++)
{
var currentBrowser = tabBrowser.getBrowserAtIndex(index);
if (callback(tabBrowser.mTabs[index], currentBrowser))
return true;
}
return false;
}
this.safeGetWindowLocation = function(window)
{
try
{
if (window)
{
if (window.closed)
return "about:closed";
if ("location" in window)
{
if ("toString" in window.location)
return window.location.toString();
else
return "(window.location has no toString)";
}
else
return "(no window.location)";
}
else
return "(no context.window)";
}
catch(exc)
{
FBTrace.sysout("TabContext.getWindowLocation failed window:", window);
return "(getWindowLocation: "+exc+")";
}
};
this.safeGetRequestName = function(request)
{
try
{
return request.name;
}
catch (exc)
{
}
return null;
}
this.safeGetContentType = function(request)
{
try
{
return new String(request.contentType).toLowerCase();
}
catch (err)
{
}
return null;
}
// ************************************************************************************************
// JavaScript Parsing
this.getExpressionAt = function(text, charOffset)
{
var offset = 0;
for (var m = reWord.exec(text); m; m = reWord.exec(text.substr(offset)))
{
var word = m[0];
var wordOffset = offset+m.index;
if (charOffset >= wordOffset && charOffset <= wordOffset+word.length)
{
var innerOffset = charOffset-wordOffset;
var dots = word.substr(0, innerOffset).split(".").length;
var subExpr = word.split(".").slice(0, dots).join(".");
return {expr: subExpr, offset: wordOffset};
}
offset = wordOffset+word.length;
}
return {expr: null, offset: -1};
};
var jsKeywords =
{
"var": 1,
"const": 1,
"class": 1,
"extends": 1,
"import": 1,
"namespace": 1,
"function": 1,
"debugger": 1,
"new": 1,
"delete": 1,
"null": 1,
"undefined": 1,
"true": 1,
"false": 1,
"void": 1,
"typeof": 1,
"instanceof": 1,
"break": 1,
"continue": 1,
"return": 1,
"throw": 1,
"try": 1,
"catch": 1,
"finally": 1,
"if": 1,
"else": 1,
"for": 1,
"while": 1,
"do": 1,
"with": 1,
"switch": 1,
"case": 1,
"default": 1
};
this.isJavaScriptKeyword = function(name)
{
return name in jsKeywords;
};
// ************************************************************************************************
// Events
this.cancelEvent = function(event)
{
event.stopPropagation();
event.preventDefault();
};
this.isLeftClick = function(event)
{
return event.button == 0 && this.noKeyModifiers(event);
};
this.isMiddleClick = function(event)
{
return event.button == 1 && this.noKeyModifiers(event);
};
this.isRightClick = function(event)
{
return event.button == 2 && this.noKeyModifiers(event);
};
this.noKeyModifiers = function(event)
{
return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
};
this.isControlClick = function(event)
{
return event.button == 0 && this.isControl(event);
};
this.isShiftClick = function(event)
{
return event.button == 0 && this.isShift(event);
};
this.isControl = function(event)
{
return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
};
this.isAlt = function(event)
{
return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey;
};
this.isAltClick = function(event)
{
return event.button == 0 && this.isAlt(event);
};
this.isControlShift = function(event)
{
return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
};
this.isShift = function(event)
{
return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
};
this.dispatch = function(listeners, name, args)
{
if (!listeners)
return;
try {
for (var i = 0; i < listeners.length; ++i)
{
var listener = listeners[i];
if ( listener[name] )
{
//FBTrace.sysout("FBL.dispatch "+i+") "+name+" to "+listener.dispatchName);
try
{
listener[name].apply(listener, args);
}
catch(exc)
{
}
}
else
{
}
}
}
catch (exc)
{
}
};
this.dispatch2 = function(listeners, name, args)
{
try
{
for (var i = 0; i < listeners.length; ++i)
{
var listener = listeners[i];
if ( listener.hasOwnProperty(name) )
{
var result = listener[name].apply(listener, args);
if ( result )
{
return result;
}
}
else
{
}
}
}
catch (exc)
{
}
};
// ************************************************************************************************
// DOM Events
const eventTypes =
{
composition: [
"composition",
"compositionstart",
"compositionend" ],
contextmenu: [
"contextmenu" ],
drag: [
"dragenter",
"dragover",
"dragexit",
"dragdrop",
"draggesture" ],
focus: [
"focus",
"blur" ],
form: [
"submit",
"reset",
"change",
"select",
"input" ],
key: [
"keydown",
"keyup",
"keypress" ],
load: [
"load",
"beforeunload",
"unload",
"abort",
"error" ],
mouse: [
"mousedown",
"mouseup",
"click",
"dblclick",
"mouseover",
"mouseout",
"mousemove" ],
mutation: [
"DOMSubtreeModified",
"DOMNodeInserted",
"DOMNodeRemoved",
"DOMNodeRemovedFromDocument",
"DOMNodeInsertedIntoDocument",
"DOMAttrModified",
"DOMCharacterDataModified" ],
paint: [
"paint",
"resize",
"scroll" ],
scroll: [
"overflow",
"underflow",
"overflowchanged" ],
text: [
"text" ],
ui: [
"DOMActivate",
"DOMFocusIn",
"DOMFocusOut" ],
xul: [
"popupshowing",
"popupshown",
"popuphiding",
"popuphidden",
"close",
"command",
"broadcast",
"commandupdate" ]
};
this.getEventFamily = function(eventType)
{
if (!this.families)
{
this.families = {};
for (var family in eventTypes)
{
var types = eventTypes[family];
for (var i = 0; i < types.length; ++i)
this.families[types[i]] = family;
}
}
return this.families[eventType];
};
this.attachAllListeners = function(object, listener)
{
for (var family in eventTypes)
{
if (family != "mutation" || Firebug.attachMutationEvents)
this.attachFamilyListeners(family, object, listener);
}
};
this.detachAllListeners = function(object, listener)
{
for (var family in eventTypes)
{
if (family != "mutation" || Firebug.attachMutationEvents)
this.detachFamilyListeners(family, object, listener);
}
};
this.attachFamilyListeners = function(family, object, listener)
{
var types = eventTypes[family];
for (var i = 0; i < types.length; ++i)
object.addEventListener(types[i], listener, false);
};
this.detachFamilyListeners = function(family, object, listener)
{
var types = eventTypes[family];
for (var i = 0; i < types.length; ++i)
object.removeEventListener(types[i], listener, false);
};
// ************************************************************************************************
// URLs
this.getFileName = function(url)
{
var split = this.splitURLBase(url);
return split.name;
};
this.splitURLBase = function(url)
{
if (this.isDataURL(url))
return this.splitDataURL(url);
return this.splitURLTrue(url);
};
this.splitDataURL = function(url)
{
var mark = url.indexOf('data:');
if (mark != 0)
return false; // the first 5 chars must be 'data:'
var point = url.indexOf(',', 5);
if (point < 5)
return false; // syntax error
var props = { encodedContent: url.substr(point+1) };
var metadataBuffer = url.substring(5, point);
var metadata = metadataBuffer.split(';');
for (var i = 0; i < metadata.length; i++)
{
var nv = metadata[i].split('=');
if (nv.length == 2)
props[nv[0]] = nv[1];
}
// Additional Firebug-specific properties
if (props.hasOwnProperty('fileName'))
{
var caller_URL = decodeURIComponent(props['fileName']);
var caller_split = this.splitURLTrue(caller_URL);
props['fileName'] = caller_URL;
if (props.hasOwnProperty('baseLineNumber')) // this means it's probably an eval()
{
props['path'] = caller_split.path;
props['line'] = props['baseLineNumber'];
var hint = decodeURIComponent(props['encodedContent']).substr(0,200).replace(/\s*$/, "");
props['name'] = 'eval->'+hint;
}
else
{
props['name'] = caller_split.name;
props['path'] = caller_split.path;
}
}
else
{
if (!props.hasOwnProperty('path'))
props['path'] = "data:";
if (!props.hasOwnProperty('name'))
props['name'] = decodeURIComponent(props['encodedContent']).substr(0,200).replace(/\s*$/, "");
}
return props;
};
this.splitURLTrue = function(url)
{
var m = reSplitFile.exec(url);
if (!m)
return {name: url, path: url};
else if (!m[2])
return {path: m[1], name: m[1]};
else
return {path: m[1], name: m[2]+m[3]};
};
this.getFileExtension = function(url)
{
if (!url)
return null;
// Remove query string from the URL if any.
var queryString = url.indexOf("?");
if (queryString != -1)
url = url.substr(0, queryString);
// Now get the file extension.
var lastDot = url.lastIndexOf(".");
return url.substr(lastDot+1);
};
this.isSystemURL = function(url)
{
if (!url) return true;
if (url.length == 0) return true;
if (url[0] == 'h') return false;
if (url.substr(0, 9) == "resource:")
return true;
else if (url.substr(0, 16) == "chrome://firebug")
return true;
else if (url == "XPCSafeJSObjectWrapper.cpp")
return true;
else if (url.substr(0, 6) == "about:")
return true;
else if (url.indexOf("firebug-service.js") != -1)
return true;
else
return false;
};
this.isSystemPage = function(win)
{
try
{
var doc = win.document;
if (!doc)
return false;
// Detect pages for pretty printed XML
if ((doc.styleSheets.length && doc.styleSheets[0].href
== "chrome://global/content/xml/XMLPrettyPrint.css")
|| (doc.styleSheets.length > 1 && doc.styleSheets[1].href
== "chrome://browser/skin/feeds/subscribe.css"))
return true;
return FBL.isSystemURL(win.location.href);
}
catch (exc)
{
// Sometimes documents just aren't ready to be manipulated here, but don't let that
// gum up the works
ERROR("tabWatcher.isSystemPage document not ready:"+ exc);
return false;
}
}
this.isSystemStyleSheet = function(sheet)
{
var href = sheet && sheet.href;
return href && FBL.isSystemURL(href);
};
this.getURIHost = function(uri)
{
try
{
if (uri)
return uri.host;
else
return "";
}
catch (exc)
{
return "";
}
}
this.isLocalURL = function(url)
{
if (url.substr(0, 5) == "file:")
return true;
else if (url.substr(0, 8) == "wyciwyg:")
return true;
else
return false;
};
this.isDataURL = function(url)
{
return (url && url.substr(0,5) == "data:");
};
this.getLocalPath = function(url)
{
if (this.isLocalURL(url))
{
var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
var file = fileHandler.getFileFromURLSpec(url);
return file.path;
}
};
this.getURLFromLocalFile = function(file)
{
var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
var URL = fileHandler.getURLSpecFromFile(file);
return URL;
};
this.getDataURLForContent = function(content, url)
{
// data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
var uri = "data:text/html;";
uri += "fileName="+encodeURIComponent(url)+ ","
uri += encodeURIComponent(content);
return uri;
},
this.getDomain = function(url)
{
var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
return m ? m[1] : "";
};
this.getURLPath = function(url)
{
var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
return m ? m[1] : "";
};
this.getPrettyDomain = function(url)
{
var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url);
return m ? m[2] : "";
};
this.absoluteURL = function(url, baseURL)
{
return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
};
this.absoluteURLWithDots = function(url, baseURL)
{
if (url[0] == "?")
return baseURL + url;
var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;
var m = reURL.exec(url);
if (m)
return url;
var m = reURL.exec(baseURL);
if (!m)
return "";
var head = m[1];
var tail = m[3];
if (url.substr(0, 2) == "//")
return m[2] + url;
else if (url[0] == "/")
{
return head + url;
}
else if (tail[tail.length-1] == "/")
return baseURL + url;
else
{
var parts = tail.split("/");
return head + parts.slice(0, parts.length-1).join("/") + "/" + url;
}
}
this.normalizeURL = function(url) // this gets called a lot, any performance improvement welcome
{
if (!url)
return "";
// Replace one or more characters that are not forward-slash followed by /.., by space.
if (url.length < 255) // guard against monsters.
{
// Replace one or more characters that are not forward-slash followed by /.., by space.
url = url.replace(/[^/]+\/\.\.\//, "", "g");
// Issue 1496, avoid #
url = url.replace(/#.*/,"");
// For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they
// don't match up with the URLs we get back from the DOM
url = url.replace(/file:\/([^/])/g, "file:///$1");
if (url.indexOf('chrome:')==0)
{
var m = reChromeCase.exec(url); // 1 is package name, 2 is path
if (m)
{
url = "chrome://"+m[1].toLowerCase()+"/"+m[2];
}
}
}
return url;
};
this.denormalizeURL = function(url)
{
return url.replace(/file:\/\/\//g, "file:/");
};
this.parseURLParams = function(url)
{
var q = url ? url.indexOf("?") : -1;
if (q == -1)
return [];
var search = url.substr(q+1);
var h = search.lastIndexOf("#");
if (h != -1)
search = search.substr(0, h);
if (!search)
return [];
return this.parseURLEncodedText(search);
};
this.parseURLEncodedText = function(text, noLimit)
{
const maxValueLength = 25000;
var params = [];
// Unescape '+' characters that are used to encode a space.
// See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
text = text.replace(/\+/g, " ");
function decodeText(text)
{
try
{
return decodeURIComponent(text);
}
catch (e)
{
return decodeURIComponent(unescape(text));
}
}
var args = text.split("&");
for (var i = 0; i < args.length; ++i)
{
try
{
var index = args[i].indexOf("=");
if (index != -1)
{
var paramName = args[i].substring(0, index);
var paramValue = args[i].substring(index + 1);
if (paramValue.length > maxValueLength && !noLimit)
paramValue = this.$STR("LargeData");
params.push({name: decodeText(paramName), value: decodeText(paramValue)});
}
else
{
var paramName = args[i];
params.push({name: decodeText(paramName), value: ""});
}
}
catch (e)
{
}
}
params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
return params;
};
this.reEncodeURL = function(file, text, noLimit)
{
var lines = text.split("\n");
var params = this.parseURLEncodedText(lines[lines.length-1], noLimit);
var args = [];
for (var i = 0; i < params.length; ++i)
args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value));
var url = file.href;
url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
return url;
};
this.getResource = function(aURL)
{
try
{
var channel=ioService.newChannel(aURL,null,null);
var input=channel.open();
return FBL.readFromStream(input);
}
catch (e)
{
}
};
// ************************************************************************************************
// JSON
this.parseJSONString = function(jsonString, originURL)
{
var regex = new RegExp(/^\/\*-secure-([\s\S]*)\*\/\s*$/);
var matches = regex.exec(jsonString);
if (matches)
{
jsonString = matches[1];
if (jsonString[0] == "\\" && jsonString[1] == "n")
jsonString = jsonString.substr(2);
if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n")
jsonString = jsonString.substr(0, jsonString.length-2);
}
if (jsonString.indexOf("&&&START&&&"))
{
regex = new RegExp(/&&&START&&& (.+) &&&END&&&/);
matches = regex.exec(jsonString);
if (matches)
jsonString = matches[1];
}
try
{
var s = Components.utils.Sandbox(originURL);
// throw on the extra parentheses
return Components.utils.evalInSandbox("(" + jsonString + ")", s);
}
catch(e)
{
}
// Let's try to parse it as JSONP.
var reJSONP = /^\s*([A-Za-z0-9_.]+\s*(?:\[.*\]|))\s*\(.*\)/;
var m = reJSONP.exec(jsonString);
if (!m || !m[1])
return null;
var callbackName = m[1];
jsonString = jsonString.replace(callbackName, "callback");
try
{
var s = Components.utils.Sandbox(originURL);
s["callback"] = function(object) { return object; };
return Components.utils.evalInSandbox(jsonString, s);
}
catch(ex)
{
}
return null;
};
this.parseJSONPString = function(jsonString, originURL)
{
}
// ************************************************************************************************
// Network
this.readFromStream = function(stream, charset, noClose)
{
var sis = this.CCSV("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
sis.setInputStream(stream);
var segments = [];
for (var count = stream.available(); count; count = stream.available())
segments.push(sis.readBytes(count));
if (!noClose)
sis.close();
var text = segments.join("");
try
{
return this.convertToUnicode(text, charset);
}
catch (err)
{
}
return text;
};
this.readPostTextFromPage = function(url, context)
{
if (url == context.browser.contentWindow.location.href)
{
try
{
var webNav = context.browser.webNavigation;
var descriptor = this.QI(webNav, Ci.nsIWebPageDescriptor).currentDescriptor;
var entry = this.QI(descriptor, Ci.nsISHEntry);
if (entry && entry.postData)
{
var postStream = this.QI(entry.postData, Ci.nsISeekableStream);
postStream.seek(NS_SEEK_SET, 0);
var charset = context.window.document.characterSet;
return this.readFromStream(postStream, charset, true);
}
}
catch (exc)
{
}
}
};
this.readPostTextFromRequest = function(request, context)
{
try
{
var is = this.QI(request, Ci.nsIUploadChannel).uploadStream;
if (is)
{
var ss = this.QI(is, Ci.nsISeekableStream);
var prevOffset;
if (ss)
{
prevOffset = ss.tell();
ss.seek(NS_SEEK_SET, 0);
}
// Read data from the stream..
var charset = (context && context.window) ? context.window.document.characterSet : null;
var text = this.readFromStream(is, charset, true);
// Seek locks the file so, seek to the beginning only if necko hasn't read it yet,
// since necko doesn't seek to 0 before reading (at lest not till 459384 is fixed).
if (ss && prevOffset == 0)
ss.seek(NS_SEEK_SET, 0);
return text;
}
}
catch(exc)
{
}
return null;
};
this.getInputStreamFromString = function(dataString)
{
var stringStream = this.CCIN("@mozilla.org/io/string-input-stream;1", "nsIStringInputStream");
if ("data" in stringStream) // Gecko 1.9 or newer
stringStream.data = dataString;
else // 1.8 or older
stringStream.setData(dataString, dataString.length);
return stringStream;
};
this.getWindowForRequest = function(request)
{
var webProgress = this.getRequestWebProgress(request);
try {
if (webProgress)
return webProgress.DOMWindow;
}
catch (ex) {
}
return null;
};
this.getRequestWebProgress = function(request)
{
try
{
if (request && request.notificationCallbacks)
return request.notificationCallbacks.getInterface(Ci.nsIWebProgress);
} catch (exc) {}
try
{
if (request && request.loadGroup && request.loadGroup.groupObserver)
return request.loadGroup.groupObserver.QueryInterface(Ci.nsIWebProgress);
} catch (exc) {}
return null;
};
/**
* Returns <browser> element for specified content window.
* @param {Object} win - Content window
*/
this.getBrowserForWindow = function(win)
{
var tabBrowser = document.getElementById("content");
if (tabBrowser && win.document)
return tabBrowser.getBrowserForDocument(win.document);
};
// ************************************************************************************************
this.BaseProgressListener =
{
QueryInterface : function(iid)
{
if (iid.equals(Ci.nsIWebProgressListener) ||
iid.equals(Ci.nsISupportsWeakReference) ||
iid.equals(Ci.nsISupports))
{
return this;
}
throw Components.results.NS_NOINTERFACE;
},
stateIsRequest: false,
onLocationChange: function() {},
onStateChange : function() {},
onProgressChange : function() {},
onStatusChange : function() {},
onSecurityChange : function() {},
onLinkIconAvailable : function() {}
};
// ************************************************************************************************
// Network Tracing
this.getStateDescription = function(flag)
{
var state = [];
var nsIWebProgressListener = Ci.nsIWebProgressListener;
if (flag & nsIWebProgressListener.STATE_START) state.push("STATE_START");
else if (flag & nsIWebProgressListener.STATE_REDIRECTING) state.push("STATE_REDIRECTING");
else if (flag & nsIWebProgressListener.STATE_TRANSFERRING) state.push("STATE_TRANSFERRING");
else if (flag & nsIWebProgressListener.STATE_NEGOTIATING) state.push("STATE_NEGOTIATING");
else if (flag & nsIWebProgressListener.STATE_STOP) state.push("STATE_STOP");
if (flag & nsIWebProgressListener.STATE_IS_REQUEST) state.push("STATE_IS_REQUEST");
if (flag & nsIWebProgressListener.STATE_IS_DOCUMENT) state.push("STATE_IS_DOCUMENT");
if (flag & nsIWebProgressListener.STATE_IS_NETWORK) state.push("STATE_IS_NETWORK");
if (flag & nsIWebProgressListener.STATE_IS_WINDOW) state.push("STATE_IS_WINDOW");
if (flag & nsIWebProgressListener.STATE_RESTORING) state.push("STATE_RESTORING");
if (flag & nsIWebProgressListener.STATE_IS_INSECURE) state.push("STATE_IS_INSECURE");
if (flag & nsIWebProgressListener.STATE_IS_BROKEN) state.push("STATE_IS_BROKEN");
if (flag & nsIWebProgressListener.STATE_IS_SECURE) state.push("STATE_IS_SECURE");
if (flag & nsIWebProgressListener.STATE_SECURE_HIGH) state.push("STATE_SECURE_HIGH");
if (flag & nsIWebProgressListener.STATE_SECURE_MED) state.push("STATE_SECURE_MED");
if (flag & nsIWebProgressListener.STATE_SECURE_LOW) state.push("STATE_SECURE_LOW");
return state.join(", ");
};
this.getStatusDescription = function(status)
{
var nsISocketTransport = Ci.nsISocketTransport;
var nsITransport = Ci.nsITransport;
if (status == nsISocketTransport.STATUS_RESOLVING) return "STATUS_RESOLVING";
if (status == nsISocketTransport.STATUS_CONNECTING_TO) return "STATUS_CONNECTING_TO";
if (status == nsISocketTransport.STATUS_CONNECTED_TO) return "STATUS_CONNECTED_TO";
if (status == nsISocketTransport.STATUS_SENDING_TO) return "STATUS_SENDING_TO";
if (status == nsISocketTransport.STATUS_WAITING_FOR) return "STATUS_WAITING_FOR";
if (status == nsISocketTransport.STATUS_RECEIVING_FROM) return "STATUS_RECEIVING_FROM";
if (status == nsITransport.STATUS_READING) return "STATUS_READING";
if (status == nsITransport.STATUS_WRITING) return "STATUS_WRITING";
};
this.getLoadFlagsDescription = function(loadFlags)
{
var flags = [];
var nsIChannel = Ci.nsIChannel;
var nsICachingChannel = Ci.nsICachingChannel;
if (loadFlags & nsIChannel.LOAD_DOCUMENT_URI) flags.push("LOAD_DOCUMENT_URI");
if (loadFlags & nsIChannel.LOAD_RETARGETED_DOCUMENT_URI) flags.push("LOAD_RETARGETED_DOCUMENT_URI");
if (loadFlags & nsIChannel.LOAD_REPLACE) flags.push("LOAD_REPLACE");
if (loadFlags & nsIChannel.LOAD_INITIAL_DOCUMENT_URI) flags.push("LOAD_INITIAL_DOCUMENT_URI");
if (loadFlags & nsIChannel.LOAD_TARGETED) flags.push("LOAD_TARGETED");
if (loadFlags & nsIChannel.LOAD_CALL_CONTENT_SNIFFERS) flags.push("LOAD_CALL_CONTENT_SNIFFERS");
if (loadFlags & nsICachingChannel.LOAD_NO_NETWORK_IO) flags.push("LOAD_NO_NETWORK_IO");
if (loadFlags & nsICachingChannel.LOAD_CHECK_OFFLINE_CACHE) flags.push("LOAD_CHECK_OFFLINE_CACHE");
if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE) flags.push("LOAD_BYPASS_LOCAL_CACHE");
if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) flags.push("LOAD_BYPASS_LOCAL_CACHE_IF_BUSY");
if (loadFlags & nsICachingChannel.LOAD_ONLY_FROM_CACHE) flags.push("LOAD_ONLY_FROM_CACHE");
if (loadFlags & nsICachingChannel.LOAD_ONLY_IF_MODIFIED) flags.push("LOAD_ONLY_IF_MODIFIED");
return flags.join(", ");
};
// ************************************************************************************************
// Programs
this.launchProgram = function(exePath, args)
{
try {
var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
file.initWithPath(exePath);
if (this.getPlatformName() == "Darwin" && file.isDirectory())
{
args = this.extendArray(["-a", exePath], args);
file.initWithPath("/usr/bin/open");
}
if (!file.exists())
return false;
var process = this.CCIN("@mozilla.org/process/util;1", "nsIProcess");
process.init(file);
process.run(false, args, args.length, {});
return true;
}
catch(exc)
{
this.ERROR(exc);
}
return false;
};
this.getIconURLForFile = function(path)
{
var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
try {
var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
file.initWithPath(path);
if ((this.getPlatformName() == "Darwin") && !file.isDirectory() && (path.indexOf(".app/") != -1))
{
path = path.substr(0,path.lastIndexOf(".app/")+4);
file.initWithPath(path);
}
return "moz-icon://" + fileHandler.getURLSpecFromFile(file) + "?size=16";
}
catch(exc)
{
this.ERROR(exc);
}
return null;
}
this.makeURI = function(urlString)
{
try
{
if (urlString)
return ioService.newURI(urlString, null, null);
}
catch(exc)
{
//var explain = {message: "Firebug.lib.makeURI FAILS", url: urlString, exception: exc};
// todo convert explain to json and then to data url
return false;
}
}
// ************************************************************************************************
this.persistObjects = function(panel, panelState)
{
// Persist the location and selection so we can restore them in case of a reload
if (panel.location)
panelState.persistedLocation = this.persistObject(panel.location, panel.context); // fn(context)->location
if (panel.selection)
panelState.persistedSelection = this.persistObject(panel.selection, panel.context);
};
this.persistObject = function(object, context)
{
var rep = Firebug.getRep(object);
return rep ? rep.persistObject(object, context) : null;
};
this.restoreLocation = function(panel, panelState)
{
var restored = false;
if (!panel.location && panelState && panelState.persistedLocation)
{
var location = panelState.persistedLocation(panel.context);
if (location)
{
panel.navigate(location);
restored = true;
}
}
if (!panel.location)
panel.navigate(null);
return restored;
};
this.restoreSelection = function(panel, panelState)
{
var needRetry = false;
if (!panel.selection && panelState && panelState.persistedSelection)
{
var selection = panelState.persistedSelection(panel.context);
if (selection)
panel.select(selection);
else
needRetry = true;
}
if (!panel.selection) // Couldn't restore the selection, so select the default object
panel.select(null);
if (needRetry)
{
function overrideDefaultWithPersistedSelection()
{
if (panel.selection == panel.getDefaultSelection(panel.context) && panelState.persistedSelection)
{
var selection = panelState.persistedSelection(panel.context);
if (selection)
panel.select(selection);
}
}
// If we couldn't restore the selection, wait a bit and try again
panel.context.setTimeout(overrideDefaultWithPersistedSelection, overrideDefaultsWithPersistedValuesTimeout);
}
};
this.restoreObjects = function(panel, panelState)
{
this.restoreLocation(panel, panelState);
this.restoreSelection(panel, panelState);
};
this.getPersistedState = function(context, panelName)
{
if (!context)
return null;
var persistedState = context.persistedState;
if (!persistedState)
persistedState = context.persistedState = {};
if (!persistedState.panelState)
persistedState.panelState = {};
var panelState = persistedState.panelState[panelName];
if (!panelState)
panelState = persistedState.panelState[panelName] = {};
return panelState;
};
// ************************************************************************************************
this.ErrorMessage = function(message, href, lineNo, source, category, context, trace, msgId)
{
this.message = message;
this.href = href;
this.lineNo = lineNo;
this.source = source;
this.category = category;
this.context = context;
this.trace = trace;
this.msgId = msgId;
};
this.ErrorMessage.prototype =
{
getSourceLine: function()
{
return this.context.sourceCache.getLine(this.href, this.lineNo);
},
resetSource: function()
{
if (this.href && this.lineNo)
this.source = this.getSourceLine();
},
correctWithStackTrace: function(trace)
{
var frame = trace.frames[0];
if (frame)
{
this.href = frame.href;
this.lineNo = frame.line;
this.trace = trace;
}
},
correctSourcePoint: function(sourceName, lineNumber)
{
this.href = sourceName;
this.lineNo = lineNumber;
},
};
// ************************************************************************************************
/**
* @class Searches for text in a given node.
*
* @constructor
* @param {Node} rootNode Node to search
* @param {Function} rowFinder results filter. On find this method will be called
* with the node containing the matched text as the first parameter. This may
* be undefined to return the node as is.
*/
this.TextSearch = function(rootNode, rowFinder)
{
var doc = rootNode.ownerDocument;
var count, searchRange, startPt;
/**
* Find the first result in the node.
*
* @param {String} text Text to search for
* @param {boolean} reverse true to perform a reverse search
* @param {boolean} caseSensitive true to perform a case sensitive search
*/
this.find = function(text, reverse, caseSensitive)
{
this.text = text;
finder.findBackwards = !!reverse;
finder.caseSensitive = !!caseSensitive;
var range = this.range = finder.Find(
text, searchRange,
startPt || searchRange,
searchRange);
var match = range ? range.startContainer : null;
return this.currentNode = (rowFinder && match ? rowFinder(match) : match);
};
/**
* Find the next search result
*
* @param {boolean} wrapAround true to wrap the search if the end of range is reached
* @param {boolean} sameNode true to return multiple results from the same text node
* @param {boolean} reverse true to search in reverse
* @param {boolean} caseSensitive true to perform a case sensitive search
*/
this.findNext = function(wrapAround, sameNode, reverse, caseSensitive)
{
startPt = undefined;
if (sameNode && this.range)
{
startPt = this.range.cloneRange();
if (reverse)
{
startPt.setEnd(startPt.startContainer, startPt.startOffset);
}
else
{
startPt.setStart(startPt.startContainer, startPt.startOffset+1);
}
}
if (!startPt)
{
var curNode = this.currentNode ? this.currentNode : rootNode;
startPt = doc.createRange();
try
{
if (reverse)
{
startPt.setStartBefore(curNode);
}
else
{
startPt.setStartAfter(curNode);
}
}
catch (e)
{
try {
FBTrace.sysout("setStart try\n");
startPt.setStart(curNode);
FBTrace.sysout("setStart success\n");
} catch (exc) {
return;
}
}
}
var match = startPt && this.find(this.text, reverse, caseSensitive);
if (!match && wrapAround)
{
this.reset();
return this.find(this.text, reverse, caseSensitive);
}
return match;
};
/**
* Resets the instance state to the initial state.
*/
this.reset = function()
{
searchRange = doc.createRange();
searchRange.selectNode(rootNode);
startPt = searchRange;
};
this.reset();
};
// ************************************************************************************************
this.SourceBoxTextSearch = function(sourceBox)
{
this.find = function(text, reverse, caseSensitive)
{
this.text = text;
this.re = new FBL.ReversibleRegExp(text);
return this.findNext(false, reverse, caseSensitive);
};
this.findNext = function(wrapAround, reverse, caseSensitive)
{
var lines = sourceBox.lines;
var match = null;
for (var iter = new FBL.ReversibleIterator(lines.length, this.mark, reverse); iter.next();)
{
match = this.re.exec(lines[iter.index], false, caseSensitive);
if (match)
{
this.mark = iter.index;
return iter.index;
}
}
if (!match && wrapAround)
{
this.reset();
return this.findNext(false, reverse, caseSensitive);
}
return match;
};
this.reset = function()
{
delete this.mark;
};
this.reset();
};
//************************************************************************************************
this.Continued = function()
{
};
this.Continued.prototype =
{
complete: function()
{
if (this.callback)
this.callback.apply(top, arguments);
else
this.result = cloneArray(arguments);
},
wait: function(cb)
{
if ("result" in this)
cb.apply(top, this.result);
else
this.callback = cb;
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.SourceLink = function(url, line, type, object, instance)
{
this.href = url;
this.instance = instance;
this.line = line;
this.type = type;
this.object = object;
};
this.SourceLink.prototype =
{
toString: function()
{
return this.href;
},
toJSON: function() // until 3.1...
{
return "{\"href\":\""+this.href+"\", "+
(this.line?("\"line\":"+this.line+","):"")+
(this.type?(" \"type\":\""+this.type+"\","):"")+
"}";
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.SourceText = function(lines, owner)
{
this.lines = lines;
this.owner = owner;
};
this.SourceText.getLineAsHTML = function(lineNo)
{
return escapeForSourceLine(this.lines[lineNo-1]);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.StackTrace = function()
{
this.frames = [];
};
this.StackTrace.prototype =
{
toString: function()
{
var trace = "<top>\n";
for (var i = 0; i < this.frames.length; i++)
{
trace += "[" + i + "]"+ this.frames[i]+"\n";
}
trace += "<bottom>\n";
return trace;
},
reverse: function()
{
this.frames.reverse();
return this;
},
destroy: function()
{
for (var i = 0; i < this.frames.length; i++)
{
this.frames[i].destroy();
}
}
};
this.traceToString = function(trace) /*@explore*/
{ /*@explore*/
var str = "<top>"; /*@explore*/
for(var i = 0; i < trace.frames.length; i++) /*@explore*/
str += "\n" + trace.frames[i]; /*@explore*/
str += "\n<bottom>"; /*@explore*/
return str; /*@explore*/
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.StackFrame = function(context, fn, script, href, lineNo, args, pc)
{
this.context = context;
this.fn = fn;
this.script = script;
this.href = href;
this.line = lineNo;
this.args = args;
this.flags = (script?script.flags:null);
this.pc = pc;
};
this.StackFrame.prototype =
{
toString: function()
{
// XXXjjb analyze args and fn?
if (this.script)
return "("+this.flags+")"+this.href+":"+this.script.baseLineNumber+"-"
+(this.script.baseLineNumber+this.script.lineExtent)+"@"+this.line;
else
return this.href;
},
destroy: function()
{
this.script = null;
this.fn = null;
},
signature: function()
{
return this.script.tag +"." + this.pc;
}
};
//-----------------------111111----222222-----33---444 1 All 'Not a (' followed by (; 2 All 'Not a )' followed by a ); 3 text between @ and : digits
var reErrorStackLine = /([^\(]*)\(([^\)]*)\)@(.*):(\d*)/;
this.parseToStackFrame = function(line) // function name (arg, arg, arg)@fileName:lineNo
{
var m = reErrorStackLine.exec(line);
if (m)
return new this.StackFrame(null, m[1], null, m[3], m[4], m[2].split(','), 0);
}
this.parseToStackTrace = function(stack)
{
var lines = stack.split('\n');
var trace = new this.StackTrace();
for (var i = 0; i < lines.length; i++)
{
var frame = this.parseToStackFrame(lines[i]);
FBTrace.sysout("parseToStackTrace i "+i+" line:"+lines[i]+ "->frame: "+frame, frame);
if (frame)
trace.frames.push(frame);
}
return trace;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.Property = function(object, name)
{
this.object = object;
this.name = name;
this.getObject = function()
{
return object[name];
};
};
this.ErrorCopy = function(message)
{
this.message = message;
};
function EventCopy(event)
{
// Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to
// represent them long term in the inspector.
for (var name in event)
{
try {
this[name] = event[name];
} catch (exc) { }
}
}
this.EventCopy = EventCopy;
// ************************************************************************************************
// DOM Constants
this.getDOMMembers = function(object)
{
if (!domMemberCache)
{
domMemberCache = {};
for (var name in domMemberMap)
{
var builtins = domMemberMap[name];
var cache = domMemberCache[name] = {};
for (var i = 0; i < builtins.length; ++i)
cache[builtins[i]] = i;
}
}
if (object instanceof Window)
{ return domMemberCache.Window; }
else if (object instanceof Document || object instanceof XMLDocument)
{ return domMemberCache.Document; }
else if (object instanceof Location)
{ return domMemberCache.Location; }
else if (object instanceof HTMLImageElement)
{ return domMemberCache.HTMLImageElement; }
else if (object instanceof HTMLAnchorElement)
{ return domMemberCache.HTMLAnchorElement; }
else if (object instanceof HTMLInputElement)
{ return domMemberCache.HTMLInputElement; }
else if (object instanceof HTMLButtonElement)
{ return domMemberCache.HTMLButtonElement; }
else if (object instanceof HTMLFormElement)
{ return domMemberCache.HTMLFormElement; }
else if (object instanceof HTMLBodyElement)
{ return domMemberCache.HTMLBodyElement; }
else if (object instanceof HTMLHtmlElement)
{ return domMemberCache.HTMLHtmlElement; }
else if (object instanceof HTMLScriptElement)
{ return domMemberCache.HTMLScriptElement; }
else if (object instanceof HTMLTableElement)
{ return domMemberCache.HTMLTableElement; }
else if (object instanceof HTMLTableRowElement)
{ return domMemberCache.HTMLTableRowElement; }
else if (object instanceof HTMLTableCellElement)
{ return domMemberCache.HTMLTableCellElement; }
else if (object instanceof HTMLIFrameElement)
{ return domMemberCache.HTMLIFrameElement; }
else if (object instanceof SVGSVGElement)
{ return domMemberCache.SVGSVGElement; }
else if (object instanceof SVGElement)
{ return domMemberCache.SVGElement; }
else if (object instanceof Element)
{ return domMemberCache.Element; }
else if (object instanceof Text || object instanceof CDATASection)
{ return domMemberCache.Text; }
else if (object instanceof Attr)
{ return domMemberCache.Attr; }
else if (object instanceof Node)
{ return domMemberCache.Node; }
else if (object instanceof Event || object instanceof EventCopy)
{ return domMemberCache.Event; }
else
return {};
};
this.isDOMMember = function(object, propName)
{
var members = this.getDOMMembers(object);
return members && propName in members;
};
var domMemberCache = null;
var domMemberMap = {};
domMemberMap.Window =
[
"document",
"frameElement",
"innerWidth",
"innerHeight",
"outerWidth",
"outerHeight",
"screenX",
"screenY",
"pageXOffset",
"pageYOffset",
"scrollX",
"scrollY",
"scrollMaxX",
"scrollMaxY",
"status",
"defaultStatus",
"parent",
"opener",
"top",
"window",
"content",
"self",
"location",
"history",
"frames",
"navigator",
"screen",
"menubar",
"toolbar",
"locationbar",
"personalbar",
"statusbar",
"directories",
"scrollbars",
"fullScreen",
"netscape",
"java",
"console",
"Components",
"controllers",
"closed",
"crypto",
"pkcs11",
"name",
"property",
"length",
"sessionStorage",
"globalStorage",
"setTimeout",
"setInterval",
"clearTimeout",
"clearInterval",
"addEventListener",
"removeEventListener",
"dispatchEvent",
"getComputedStyle",
"captureEvents",
"releaseEvents",
"routeEvent",
"enableExternalCapture",
"disableExternalCapture",
"moveTo",
"moveBy",
"resizeTo",
"resizeBy",
"scroll",
"scrollTo",
"scrollBy",
"scrollByLines",
"scrollByPages",
"sizeToContent",
"setResizable",
"getSelection",
"open",
"openDialog",
"close",
"alert",
"confirm",
"prompt",
"dump",
"focus",
"blur",
"find",
"back",
"forward",
"home",
"stop",
"print",
"atob",
"btoa",
"updateCommands",
"XPCNativeWrapper",
"GeckoActiveXObject",
"applicationCache", // FF3
"GetWeakReference", // Gecko
"XPCSafeJSObjectWrapper", // Gecko
"postMessage",
"localStorage", // FF3.5
"showModalDialog", // FF 3.0, MS IE4
];
domMemberMap.Location =
[
"href",
"protocol",
"host",
"hostname",
"port",
"pathname",
"search",
"hash",
"assign",
"reload",
"replace"
];
domMemberMap.Node =
[
"id",
"className",
"nodeType",
"tagName",
"nodeName",
"localName",
"prefix",
"namespaceURI",
"nodeValue",
"ownerDocument",
"parentNode",
"offsetParent",
"nextSibling",
"previousSibling",
"firstChild",
"lastChild",
"childNodes",
"attributes",
"dir",
"baseURI",
"textContent",
"innerHTML",
"addEventListener",
"removeEventListener",
"dispatchEvent",
"cloneNode",
"appendChild",
"insertBefore",
"replaceChild",
"removeChild",
"compareDocumentPosition",
"hasAttributes",
"hasChildNodes",
"lookupNamespaceURI",
"lookupPrefix",
"normalize",
"isDefaultNamespace",
"isEqualNode",
"isSameNode",
"isSupported",
"getFeature",
"getUserData",
"setUserData"
];
domMemberMap.Document = extendArray(domMemberMap.Node,
[
"documentElement",
"body",
"title",
"location",
"referrer",
"cookie",
"contentType",
"lastModified",
"characterSet",
"inputEncoding",
"xmlEncoding",
"xmlStandalone",
"xmlVersion",
"strictErrorChecking",
"documentURI",
"URL",
"defaultView",
"doctype",
"implementation",
"styleSheets",
"images",
"links",
"forms",
"anchors",
"embeds",
"plugins",
"applets",
"width",
"height",
"designMode",
"compatMode",
"async",
"preferredStylesheetSet",
"alinkColor",
"linkColor",
"vlinkColor",
"bgColor",
"fgColor",
"domain",
"addEventListener",
"removeEventListener",
"dispatchEvent",
"captureEvents",
"releaseEvents",
"routeEvent",
"clear",
"open",
"close",
"execCommand",
"execCommandShowHelp",
"getElementsByName",
"getSelection",
"queryCommandEnabled",
"queryCommandIndeterm",
"queryCommandState",
"queryCommandSupported",
"queryCommandText",
"queryCommandValue",
"write",
"writeln",
"adoptNode",
"appendChild",
"removeChild",
"renameNode",
"cloneNode",
"compareDocumentPosition",
"createAttribute",
"createAttributeNS",
"createCDATASection",
"createComment",
"createDocumentFragment",
"createElement",
"createElementNS",
"createEntityReference",
"createEvent",
"createExpression",
"createNSResolver",
"createNodeIterator",
"createProcessingInstruction",
"createRange",
"createTextNode",
"createTreeWalker",
"domConfig",
"evaluate",
"evaluateFIXptr",
"evaluateXPointer",
"getAnonymousElementByAttribute",
"getAnonymousNodes",
"addBinding",
"removeBinding",
"getBindingParent",
"getBoxObjectFor",
"setBoxObjectFor",
"getElementById",
"getElementsByTagName",
"getElementsByTagNameNS",
"hasAttributes",
"hasChildNodes",
"importNode",
"insertBefore",
"isDefaultNamespace",
"isEqualNode",
"isSameNode",
"isSupported",
"load",
"loadBindingDocument",
"lookupNamespaceURI",
"lookupPrefix",
"normalize",
"normalizeDocument",
"getFeature",
"getUserData",
"setUserData"
]);
domMemberMap.Element = extendArray(domMemberMap.Node,
[
"clientWidth",
"clientHeight",
"offsetLeft",
"offsetTop",
"offsetWidth",
"offsetHeight",
"scrollLeft",
"scrollTop",
"scrollWidth",
"scrollHeight",
"style",
"tabIndex",
"title",
"lang",
"align",
"spellcheck",
"addEventListener",
"removeEventListener",
"dispatchEvent",
"focus",
"blur",
"cloneNode",
"appendChild",
"insertBefore",
"replaceChild",
"removeChild",
"compareDocumentPosition",
"getElementsByTagName",
"getElementsByTagNameNS",
"getAttribute",
"getAttributeNS",
"getAttributeNode",
"getAttributeNodeNS",
"setAttribute",
"setAttributeNS",
"setAttributeNode",
"setAttributeNodeNS",
"removeAttribute",
"removeAttributeNS",
"removeAttributeNode",
"hasAttribute",
"hasAttributeNS",
"hasAttributes",
"hasChildNodes",
"lookupNamespaceURI",
"lookupPrefix",
"normalize",
"isDefaultNamespace",
"isEqualNode",
"isSameNode",
"isSupported",
"getFeature",
"getUserData",
"setUserData"
]);
domMemberMap.SVGElement = extendArray(domMemberMap.Element,
[
"x",
"y",
"width",
"height",
"rx",
"ry",
"transform",
"href",
"ownerSVGElement",
"viewportElement",
"farthestViewportElement",
"nearestViewportElement",
"getBBox",
"getCTM",
"getScreenCTM",
"getTransformToElement",
"getPresentationAttribute",
"preserveAspectRatio"
]);
domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element,
[
"x",
"y",
"width",
"height",
"rx",
"ry",
"transform",
"viewBox",
"viewport",
"currentView",
"useCurrentView",
"pixelUnitToMillimeterX",
"pixelUnitToMillimeterY",
"screenPixelToMillimeterX",
"screenPixelToMillimeterY",
"currentScale",
"currentTranslate",
"zoomAndPan",
"ownerSVGElement",
"viewportElement",
"farthestViewportElement",
"nearestViewportElement",
"contentScriptType",
"contentStyleType",
"getBBox",
"getCTM",
"getScreenCTM",
"getTransformToElement",
"getEnclosureList",
"getIntersectionList",
"getViewboxToViewportTransform",
"getPresentationAttribute",
"getElementById",
"checkEnclosure",
"checkIntersection",
"createSVGAngle",
"createSVGLength",
"createSVGMatrix",
"createSVGNumber",
"createSVGPoint",
"createSVGRect",
"createSVGString",
"createSVGTransform",
"createSVGTransformFromMatrix",
"deSelectAll",
"preserveAspectRatio",
"forceRedraw",
"suspendRedraw",
"unsuspendRedraw",
"unsuspendRedrawAll",
"getCurrentTime",
"setCurrentTime",
"animationsPaused",
"pauseAnimations",
"unpauseAnimations"
]);
domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element,
[
"src",
"naturalWidth",
"naturalHeight",
"width",
"height",
"x",
"y",
"name",
"alt",
"longDesc",
"lowsrc",
"border",
"complete",
"hspace",
"vspace",
"isMap",
"useMap",
]);
domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element,
[
"name",
"target",
"accessKey",
"href",
"protocol",
"host",
"hostname",
"port",
"pathname",
"search",
"hash",
"hreflang",
"coords",
"shape",
"text",
"type",
"rel",
"rev",
"charset"
]);
domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element,
[
"contentDocument",
"contentWindow",
"frameBorder",
"height",
"longDesc",
"marginHeight",
"marginWidth",
"name",
"scrolling",
"src",
"width"
]);
domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element,
[
"bgColor",
"border",
"caption",
"cellPadding",
"cellSpacing",
"frame",
"rows",
"rules",
"summary",
"tBodies",
"tFoot",
"tHead",
"width",
"createCaption",
"createTFoot",
"createTHead",
"deleteCaption",
"deleteRow",
"deleteTFoot",
"deleteTHead",
"insertRow"
]);
domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element,
[
"bgColor",
"cells",
"ch",
"chOff",
"rowIndex",
"sectionRowIndex",
"vAlign",
"deleteCell",
"insertCell"
]);
domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element,
[
"abbr",
"axis",
"bgColor",
"cellIndex",
"ch",
"chOff",
"colSpan",
"headers",
"height",
"noWrap",
"rowSpan",
"scope",
"vAlign",
"width"
]);
domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
[
"src"
]);
domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
[
"accessKey",
"disabled",
"form",
"name",
"type",
"value",
"click"
]);
domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element,
[
"type",
"value",
"checked",
"accept",
"accessKey",
"alt",
"controllers",
"defaultChecked",
"defaultValue",
"disabled",
"form",
"maxLength",
"name",
"readOnly",
"selectionEnd",
"selectionStart",
"size",
"src",
"textLength",
"useMap",
"click",
"select",
"setSelectionRange"
]);
domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element,
[
"acceptCharset",
"action",
"author",
"elements",
"encoding",
"enctype",
"entry_id",
"length",
"method",
"name",
"post",
"target",
"text",
"url",
"reset",
"submit"
]);
domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
[
"aLink",
"background",
"bgColor",
"link",
"text",
"vLink"
]);
domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
[
"version"
]);
domMemberMap.Text = extendArray(domMemberMap.Node,
[
"data",
"length",
"appendData",
"deleteData",
"insertData",
"replaceData",
"splitText",
"substringData"
]);
domMemberMap.Attr = extendArray(domMemberMap.Node,
[
"name",
"value",
"specified",
"ownerElement"
]);
domMemberMap.Event =
[
"type",
"target",
"currentTarget",
"originalTarget",
"explicitOriginalTarget",
"relatedTarget",
"rangeParent",
"rangeOffset",
"view",
"keyCode",
"charCode",
"screenX",
"screenY",
"clientX",
"clientY",
"layerX",
"layerY",
"pageX",
"pageY",
"detail",
"button",
"which",
"ctrlKey",
"shiftKey",
"altKey",
"metaKey",
"eventPhase",
"timeStamp",
"bubbles",
"cancelable",
"cancelBubble",
"isTrusted",
"isChar",
"getPreventDefault",
"initEvent",
"initMouseEvent",
"initKeyEvent",
"initUIEvent",
"preventBubble",
"preventCapture",
"preventDefault",
"stopPropagation"
];
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
this.domConstantMap =
{
"ELEMENT_NODE": 1,
"ATTRIBUTE_NODE": 1,
"TEXT_NODE": 1,
"CDATA_SECTION_NODE": 1,
"ENTITY_REFERENCE_NODE": 1,
"ENTITY_NODE": 1,
"PROCESSING_INSTRUCTION_NODE": 1,
"COMMENT_NODE": 1,
"DOCUMENT_NODE": 1,
"DOCUMENT_TYPE_NODE": 1,
"DOCUMENT_FRAGMENT_NODE": 1,
"NOTATION_NODE": 1,
"DOCUMENT_POSITION_DISCONNECTED": 1,
"DOCUMENT_POSITION_PRECEDING": 1,
"DOCUMENT_POSITION_FOLLOWING": 1,
"DOCUMENT_POSITION_CONTAINS": 1,
"DOCUMENT_POSITION_CONTAINED_BY": 1,
"DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1,
"UNKNOWN_RULE": 1,
"STYLE_RULE": 1,
"CHARSET_RULE": 1,
"IMPORT_RULE": 1,
"MEDIA_RULE": 1,
"FONT_FACE_RULE": 1,
"PAGE_RULE": 1,
"CAPTURING_PHASE": 1,
"AT_TARGET": 1,
"BUBBLING_PHASE": 1,
"SCROLL_PAGE_UP": 1,
"SCROLL_PAGE_DOWN": 1,
"MOUSEUP": 1,
"MOUSEDOWN": 1,
"MOUSEOVER": 1,
"MOUSEOUT": 1,
"MOUSEMOVE": 1,
"MOUSEDRAG": 1,
"CLICK": 1,
"DBLCLICK": 1,
"KEYDOWN": 1,
"KEYUP": 1,
"KEYPRESS": 1,
"DRAGDROP": 1,
"FOCUS": 1,
"BLUR": 1,
"SELECT": 1,
"CHANGE": 1,
"RESET": 1,
"SUBMIT": 1,
"SCROLL": 1,
"LOAD": 1,
"UNLOAD": 1,
"XFER_DONE": 1,
"ABORT": 1,
"ERROR": 1,
"LOCATE": 1,
"MOVE": 1,
"RESIZE": 1,
"FORWARD": 1,
"HELP": 1,
"BACK": 1,
"TEXT": 1,
"ALT_MASK": 1,
"CONTROL_MASK": 1,
"SHIFT_MASK": 1,
"META_MASK": 1,
"DOM_VK_TAB": 1,
"DOM_VK_PAGE_UP": 1,
"DOM_VK_PAGE_DOWN": 1,
"DOM_VK_UP": 1,
"DOM_VK_DOWN": 1,
"DOM_VK_LEFT": 1,
"DOM_VK_RIGHT": 1,
"DOM_VK_CANCEL": 1,
"DOM_VK_HELP": 1,
"DOM_VK_BACK_SPACE": 1,
"DOM_VK_CLEAR": 1,
"DOM_VK_RETURN": 1,
"DOM_VK_ENTER": 1,
"DOM_VK_SHIFT": 1,
"DOM_VK_CONTROL": 1,
"DOM_VK_ALT": 1,
"DOM_VK_PAUSE": 1,
"DOM_VK_CAPS_LOCK": 1,
"DOM_VK_ESCAPE": 1,
"DOM_VK_SPACE": 1,
"DOM_VK_END": 1,
"DOM_VK_HOME": 1,
"DOM_VK_PRINTSCREEN": 1,
"DOM_VK_INSERT": 1,
"DOM_VK_DELETE": 1,
"DOM_VK_0": 1,
"DOM_VK_1": 1,
"DOM_VK_2": 1,
"DOM_VK_3": 1,
"DOM_VK_4": 1,
"DOM_VK_5": 1,
"DOM_VK_6": 1,
"DOM_VK_7": 1,
"DOM_VK_8": 1,
"DOM_VK_9": 1,
"DOM_VK_SEMICOLON": 1,
"DOM_VK_EQUALS": 1,
"DOM_VK_A": 1,
"DOM_VK_B": 1,
"DOM_VK_C": 1,
"DOM_VK_D": 1,
"DOM_VK_E": 1,
"DOM_VK_F": 1,
"DOM_VK_G": 1,
"DOM_VK_H": 1,
"DOM_VK_I": 1,
"DOM_VK_J": 1,
"DOM_VK_K": 1,
"DOM_VK_L": 1,
"DOM_VK_M": 1,
"DOM_VK_N": 1,
"DOM_VK_O": 1,
"DOM_VK_P": 1,
"DOM_VK_Q": 1,
"DOM_VK_R": 1,
"DOM_VK_S": 1,
"DOM_VK_T": 1,
"DOM_VK_U": 1,
"DOM_VK_V": 1,
"DOM_VK_W": 1,
"DOM_VK_X": 1,
"DOM_VK_Y": 1,
"DOM_VK_Z": 1,
"DOM_VK_CONTEXT_MENU": 1,
"DOM_VK_NUMPAD0": 1,
"DOM_VK_NUMPAD1": 1,
"DOM_VK_NUMPAD2": 1,
"DOM_VK_NUMPAD3": 1,
"DOM_VK_NUMPAD4": 1,
"DOM_VK_NUMPAD5": 1,
"DOM_VK_NUMPAD6": 1,
"DOM_VK_NUMPAD7": 1,
"DOM_VK_NUMPAD8": 1,
"DOM_VK_NUMPAD9": 1,
"DOM_VK_MULTIPLY": 1,
"DOM_VK_ADD": 1,
"DOM_VK_SEPARATOR": 1,
"DOM_VK_SUBTRACT": 1,
"DOM_VK_DECIMAL": 1,
"DOM_VK_DIVIDE": 1,
"DOM_VK_F1": 1,
"DOM_VK_F2": 1,
"DOM_VK_F3": 1,
"DOM_VK_F4": 1,
"DOM_VK_F5": 1,
"DOM_VK_F6": 1,
"DOM_VK_F7": 1,
"DOM_VK_F8": 1,
"DOM_VK_F9": 1,
"DOM_VK_F10": 1,
"DOM_VK_F11": 1,
"DOM_VK_F12": 1,
"DOM_VK_F13": 1,
"DOM_VK_F14": 1,
"DOM_VK_F15": 1,
"DOM_VK_F16": 1,
"DOM_VK_F17": 1,
"DOM_VK_F18": 1,
"DOM_VK_F19": 1,
"DOM_VK_F20": 1,
"DOM_VK_F21": 1,
"DOM_VK_F22": 1,
"DOM_VK_F23": 1,
"DOM_VK_F24": 1,
"DOM_VK_NUM_LOCK": 1,
"DOM_VK_SCROLL_LOCK": 1,
"DOM_VK_COMMA": 1,
"DOM_VK_PERIOD": 1,
"DOM_VK_SLASH": 1,
"DOM_VK_BACK_QUOTE": 1,
"DOM_VK_OPEN_BRACKET": 1,
"DOM_VK_BACK_SLASH": 1,
"DOM_VK_CLOSE_BRACKET": 1,
"DOM_VK_QUOTE": 1,
"DOM_VK_META": 1,
"SVG_ZOOMANDPAN_DISABLE": 1,
"SVG_ZOOMANDPAN_MAGNIFY": 1,
"SVG_ZOOMANDPAN_UNKNOWN": 1
};
this.cssInfo =
{
"background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"],
"background-attachment": ["bgAttachment"],
"background-color": ["color", "systemColor"],
"background-image": ["none"],
"background-position": ["bgPosition"],
"background-repeat": ["bgRepeat"],
"border": ["borderStyle", "thickness", "color", "systemColor", "none"],
"border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"border-collapse": ["borderCollapse"],
"border-color": ["color", "systemColor"],
"border-top-color": ["color", "systemColor"],
"border-right-color": ["color", "systemColor"],
"border-bottom-color": ["color", "systemColor"],
"border-left-color": ["color", "systemColor"],
"border-spacing": [],
"border-style": ["borderStyle"],
"border-top-style": ["borderStyle"],
"border-right-style": ["borderStyle"],
"border-bottom-style": ["borderStyle"],
"border-left-style": ["borderStyle"],
"border-width": ["thickness"],
"border-top-width": ["thickness"],
"border-right-width": ["thickness"],
"border-bottom-width": ["thickness"],
"border-left-width": ["thickness"],
"bottom": ["auto"],
"caption-side": ["captionSide"],
"clear": ["clear", "none"],
"clip": ["auto"],
"color": ["color", "systemColor"],
"content": ["content", "none"],
"counter-increment": ["none"],
"counter-reset": ["none"],
"cursor": ["cursor", "none"],
"direction": ["direction"],
"display": ["display", "none"],
"empty-cells": [],
"float": ["float", "none"],
"font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"],
"font-family": ["fontFamily"],
"font-size": ["fontSize"],
"font-size-adjust": [],
"font-stretch": [],
"font-style": ["fontStyle"],
"font-variant": ["fontVariant"],
"font-weight": ["fontWeight"],
"height": ["auto"],
"ime-mode": ["imeMode", "auto"],
"left": ["auto"],
"letter-spacing": [],
"line-height": [],
"list-style": ["listStyleType", "listStylePosition", "none"],
"list-style-image": ["none"],
"list-style-position": ["listStylePosition"],
"list-style-type": ["listStyleType", "none"],
"margin": [],
"margin-top": [],
"margin-right": [],
"margin-bottom": [],
"margin-left": [],
"marker-offset": ["auto"],
"min-height": ["none"],
"max-height": ["none"],
"min-width": ["width", "none"],
"max-width": ["width", "none"],
"opacity": [],
"outline": ["borderStyle", "color", "systemColor", "none"],
"outline-color": ["color", "systemColor"],
"outline-style": ["borderStyle"],
"outline-width": [],
"overflow": ["overflow", "auto"],
"overflow-x": ["overflow", "auto"],
"overflow-y": ["overflow", "auto"],
"padding": [],
"padding-top": [],
"padding-right": [],
"padding-bottom": [],
"padding-left": [],
"position": ["position"],
"quotes": ["none"],
"right": ["auto"],
"table-layout": ["tableLayout", "auto"],
"text-align": ["textAlign"],
"text-decoration": ["textDecoration", "none"],
"text-indent": [],
"text-rendering": ["textRendering", "auto"],
"text-shadow": [],
"text-transform": ["textTransform", "none"],
"top": ["auto"],
"unicode-bidi": [],
"vertical-align": ["verticalAlign"],
"visibility": ["visibility"],
"white-space": ["whiteSpace"],
"width": ["width", "auto"],
"word-spacing": [],
"word-wrap": ["wordWrap"],
"z-index": [],
"-moz-appearance": ["mozAppearance"],
"-moz-border-image": ["mozBorderImage", "thickness", "none"],
"-moz-border-radius": [],
"-moz-border-radius-bottomleft": [],
"-moz-border-radius-bottomright": [],
"-moz-border-radius-topleft": [],
"-moz-border-radius-topright": [],
"-moz-border-top-colors": ["color", "systemColor"],
"-moz-border-right-colors": ["color", "systemColor"],
"-moz-border-bottom-colors": ["color", "systemColor"],
"-moz-border-left-colors": ["color", "systemColor"],
"-moz-border-start": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"-moz-border-end": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
"-moz-border-start-color": ["color", "systemColor"],
"-moz-border-end-color": ["color", "systemColor"],
"-moz-border-start-style": ["borderStyle"],
"-moz-border-end-style": ["borderStyle"],
"-moz-border-start-width": ["thickness"],
"-moz-border-end-width": ["thickness"],
"-moz-box-align": ["mozBoxAlign"],
"-moz-box-direction": ["mozBoxDirection"],
"-moz-box-flex": [],
"-moz-box-ordinal-group": [],
"-moz-box-orient": ["mozBoxOrient"],
"-moz-box-pack": ["mozBoxPack"],
"-moz-box-shadow": ["mozBoxShadow", "none"],
"-moz-box-sizing": ["mozBoxSizing"],
"-moz-user-focus": ["userFocus", "none"],
"-moz-user-input": ["userInput"],
"-moz-user-modify": [],
"-moz-user-select": ["userSelect", "none"],
"-moz-background-clip": [],
"-moz-background-inline-policy": [],
"-moz-background-origin": [],
"-moz-binding": [],
"-moz-column-count": [],
"-moz-column-gap": [],
"-moz-column-rule": ["thickness", "borderStyle", "color", "systemColor"],
"-moz-column-rule-width": ["thickness"],
"-moz-column-rule-style": ["borderStyle"],
"-moz-column-rule-color": ["color", "systemColor"],
"-moz-column-width": [],
"-moz-image-region": [],
"-moz-transform": ["mozTransformFunction", "none"],
"-moz-transform-origin": ["bgPosition"]
};
this.inheritedStyleNames =
{
"border-collapse": 1,
"border-spacing": 1,
"border-style": 1,
"caption-side": 1,
"color": 1,
"cursor": 1,
"direction": 1,
"empty-cells": 1,
"font": 1,
"font-family": 1,
"font-size-adjust": 1,
"font-size": 1,
"font-style": 1,
"font-variant": 1,
"font-weight": 1,
"letter-spacing": 1,
"line-height": 1,
"list-style": 1,
"list-style-image": 1,
"list-style-position": 1,
"list-style-type": 1,
"opacity": 1,
"quotes": 1,
"text-align": 1,
"text-decoration": 1,
"text-indent": 1,
"text-shadow": 1,
"text-transform": 1,
"white-space": 1,
"word-spacing": 1,
"word-wrap": 1
};
this.cssKeywords =
{
"appearance":
[
"button",
"button-small",
"checkbox",
"checkbox-container",
"checkbox-small",
"dialog",
"listbox",
"menuitem",
"menulist",
"menulist-button",
"menulist-textfield",
"menupopup",
"progressbar",
"radio",
"radio-container",
"radio-small",
"resizer",
"scrollbar",
"scrollbarbutton-down",
"scrollbarbutton-left",
"scrollbarbutton-right",
"scrollbarbutton-up",
"scrollbartrack-horizontal",
"scrollbartrack-vertical",
"separator",
"statusbar",
"tab",
"tab-left-edge",
"tabpanels",
"textfield",
"toolbar",
"toolbarbutton",
"toolbox",
"tooltip",
"treeheadercell",
"treeheadersortarrow",
"treeitem",
"treetwisty",
"treetwistyopen",
"treeview",
"window"
],
"systemColor":
[
"ActiveBorder",
"ActiveCaption",
"AppWorkspace",
"Background",
"ButtonFace",
"ButtonHighlight",
"ButtonShadow",
"ButtonText",
"CaptionText",
"GrayText",
"Highlight",
"HighlightText",
"InactiveBorder",
"InactiveCaption",
"InactiveCaptionText",
"InfoBackground",
"InfoText",
"Menu",
"MenuText",
"Scrollbar",
"ThreeDDarkShadow",
"ThreeDFace",
"ThreeDHighlight",
"ThreeDLightShadow",
"ThreeDShadow",
"Window",
"WindowFrame",
"WindowText",
"-moz-field",
"-moz-fieldtext",
"-moz-workspace",
"-moz-visitedhyperlinktext",
"-moz-nativehyperlinktext",
"-moz-use-text-color"
],
"color":
[
"AliceBlue",
"AntiqueWhite",
"Aqua",
"Aquamarine",
"Azure",
"Beige",
"Bisque",
"Black",
"BlanchedAlmond",
"Blue",
"BlueViolet",
"Brown",
"BurlyWood",
"CadetBlue",
"Chartreuse",
"Chocolate",
"Coral",
"CornflowerBlue",
"Cornsilk",
"Crimson",
"Cyan",
"DarkBlue",
"DarkCyan",
"DarkGoldenRod",
"DarkGray",
"DarkGreen",
"DarkKhaki",
"DarkMagenta",
"DarkOliveGreen",
"DarkOrange",
"DarkOrchid",
"DarkRed",
"DarkSalmon",
"DarkSeaGreen",
"DarkSlateBlue",
"DarkSlateGray",
"DarkTurquoise",
"DarkViolet",
"DeepPink",
"DarkSkyBlue",
"DimGray",
"DodgerBlue",
"Feldspar",
"FireBrick",
"FloralWhite",
"ForestGreen",
"Fuchsia",
"Gainsboro",
"GhostWhite",
"Gold",
"GoldenRod",
"Gray",
"Green",
"GreenYellow",
"HoneyDew",
"HotPink",
"IndianRed",
"Indigo",
"Ivory",
"Khaki",
"Lavender",
"LavenderBlush",
"LawnGreen",
"LemonChiffon",
"LightBlue",
"LightCoral",
"LightCyan",
"LightGoldenRodYellow",
"LightGrey",
"LightGreen",
"LightPink",
"LightSalmon",
"LightSeaGreen",
"LightSkyBlue",
"LightSlateBlue",
"LightSlateGray",
"LightSteelBlue",
"LightYellow",
"Lime",
"LimeGreen",
"Linen",
"Magenta",
"Maroon",
"MediumAquaMarine",
"MediumBlue",
"MediumOrchid",
"MediumPurple",
"MediumSeaGreen",
"MediumSlateBlue",
"MediumSpringGreen",
"MediumTurquoise",
"MediumVioletRed",
"MidnightBlue",
"MintCream",
"MistyRose",
"Moccasin",
"NavajoWhite",
"Navy",
"OldLace",
"Olive",
"OliveDrab",
"Orange",
"OrangeRed",
"Orchid",
"PaleGoldenRod",
"PaleGreen",
"PaleTurquoise",
"PaleVioletRed",
"PapayaWhip",
"PeachPuff",
"Peru",
"Pink",
"Plum",
"PowderBlue",
"Purple",
"Red",
"RosyBrown",
"RoyalBlue",
"SaddleBrown",
"Salmon",
"SandyBrown",
"SeaGreen",
"SeaShell",
"Sienna",
"Silver",
"SkyBlue",
"SlateBlue",
"SlateGray",
"Snow",
"SpringGreen",
"SteelBlue",
"Tan",
"Teal",
"Thistle",
"Tomato",
"Turquoise",
"Violet",
"VioletRed",
"Wheat",
"White",
"WhiteSmoke",
"Yellow",
"YellowGreen",
"transparent",
"invert"
],
"auto":
[
"auto"
],
"none":
[
"none"
],
"captionSide":
[
"top",
"bottom",
"left",
"right"
],
"clear":
[
"left",
"right",
"both"
],
"cursor":
[
"auto",
"cell",
"context-menu",
"crosshair",
"default",
"help",
"pointer",
"progress",
"move",
"e-resize",
"all-scroll",
"ne-resize",
"nw-resize",
"n-resize",
"se-resize",
"sw-resize",
"s-resize",
"w-resize",
"ew-resize",
"ns-resize",
"nesw-resize",
"nwse-resize",
"col-resize",
"row-resize",
"text",
"vertical-text",
"wait",
"alias",
"copy",
"move",
"no-drop",
"not-allowed",
"-moz-alias",
"-moz-cell",
"-moz-copy",
"-moz-grab",
"-moz-grabbing",
"-moz-contextmenu",
"-moz-zoom-in",
"-moz-zoom-out",
"-moz-spinning"
],
"direction":
[
"ltr",
"rtl"
],
"bgAttachment":
[
"scroll",
"fixed"
],
"bgPosition":
[
"top",
"center",
"bottom",
"left",
"right"
],
"bgRepeat":
[
"repeat",
"repeat-x",
"repeat-y",
"no-repeat"
],
"borderStyle":
[
"hidden",
"dotted",
"dashed",
"solid",
"double",
"groove",
"ridge",
"inset",
"outset",
"-moz-bg-inset",
"-moz-bg-outset",
"-moz-bg-solid"
],
"borderCollapse":
[
"collapse",
"separate"
],
"overflow":
[
"visible",
"hidden",
"scroll",
"-moz-scrollbars-horizontal",
"-moz-scrollbars-none",
"-moz-scrollbars-vertical"
],
"listStyleType":
[
"disc",
"circle",
"square",
"decimal",
"decimal-leading-zero",
"lower-roman",
"upper-roman",
"lower-greek",
"lower-alpha",
"lower-latin",
"upper-alpha",
"upper-latin",
"hebrew",
"armenian",
"georgian",
"cjk-ideographic",
"hiragana",
"katakana",
"hiragana-iroha",
"katakana-iroha",
"inherit"
],
"listStylePosition":
[
"inside",
"outside"
],
"content":
[
"open-quote",
"close-quote",
"no-open-quote",
"no-close-quote",
"inherit"
],
"fontStyle":
[
"normal",
"italic",
"oblique",
"inherit"
],
"fontVariant":
[
"normal",
"small-caps",
"inherit"
],
"fontWeight":
[
"normal",
"bold",
"bolder",
"lighter",
"inherit"
],
"fontSize":
[
"xx-small",
"x-small",
"small",
"medium",
"large",
"x-large",
"xx-large",
"smaller",
"larger"
],
"fontFamily":
[
"Arial",
"Comic Sans MS",
"Georgia",
"Tahoma",
"Verdana",
"Times New Roman",
"Trebuchet MS",
"Lucida Grande",
"Helvetica",
"serif",
"sans-serif",
"cursive",
"fantasy",
"monospace",
"caption",
"icon",
"menu",
"message-box",
"small-caption",
"status-bar",
"inherit"
],
"display":
[
"block",
"inline",
"inline-block",
"list-item",
"marker",
"run-in",
"compact",
"table",
"inline-table",
"table-row-group",
"table-column",
"table-column-group",
"table-header-group",
"table-footer-group",
"table-row",
"table-cell",
"table-caption",
"-moz-box",
"-moz-compact",
"-moz-deck",
"-moz-grid",
"-moz-grid-group",
"-moz-grid-line",
"-moz-groupbox",
"-moz-inline-block",
"-moz-inline-box",
"-moz-inline-grid",
"-moz-inline-stack",
"-moz-inline-table",
"-moz-marker",
"-moz-popup",
"-moz-runin",
"-moz-stack"
],
"position":
[
"static",
"relative",
"absolute",
"fixed",
"inherit"
],
"float":
[
"left",
"right"
],
"textAlign":
[
"left",
"right",
"center",
"justify"
],
"tableLayout":
[
"fixed"
],
"textDecoration":
[
"underline",
"overline",
"line-through",
"blink"
],
"textTransform":
[
"capitalize",
"lowercase",
"uppercase",
"inherit"
],
"unicodeBidi":
[
"normal",
"embed",
"bidi-override"
],
"visibility":
[
"visible",
"hidden",
"collapse",
"inherit"
],
"whiteSpace":
[
"normal",
"pre",
"nowrap",
"pre-wrap",
"pre-line",
"inherit"
],
"verticalAlign":
[
"baseline",
"sub",
"super",
"top",
"text-top",
"middle",
"bottom",
"text-bottom",
"inherit"
],
"thickness":
[
"thin",
"medium",
"thick"
],
"userFocus":
[
"ignore",
"normal"
],
"userInput":
[
"disabled",
"enabled"
],
"userSelect":
[
"normal"
],
"mozBoxSizing":
[
"content-box",
"padding-box",
"border-box"
],
"mozBoxAlign":
[
"start",
"center",
"end",
"baseline",
"stretch"
],
"mozBoxDirection":
[
"normal",
"reverse"
],
"mozBoxOrient":
[
"horizontal",
"vertical"
],
"mozBoxPack":
[
"start",
"center",
"end"
],
"mozBoxShadow":
[
"inset"
],
"mozBorderImage":
[
"stretch",
"round",
"repeat"
],
"mozTransformFunction":
[
"matrix",
"rotate",
"scale",
"scaleX",
"scaleY",
"skew",
"skewX",
"skewY",
"translate",
"translateX",
"translateY"
],
"width":
[
"-moz-max-content",
"-moz-min-content",
"-moz-fit-content",
"-moz-available"
],
"imeMode":
[
"normal",
"active",
"inactive",
"disabled"
],
"textRendering":
[
"optimizeSpeed",
"optimizeLegibility",
"geometricPrecision"
],
"wordWrap":
[
"normal",
"break-word",
"inherit"
]
};
this.nonEditableTags =
{
"HTML": 1,
"HEAD": 1,
"html": 1,
"head": 1
};
this.innerEditableTags =
{
"BODY": 1,
"body": 1
};
this.selfClosingTags =
{ // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML
"meta": 1,
"link": 1,
"area": 1,
"base": 1,
"col": 1,
"input": 1,
"img": 1,
"br": 1,
"hr": 1,
"param":1,
"embed":1
};
const invisibleTags = this.invisibleTags =
{
"HTML": 1,
"HEAD": 1,
"TITLE": 1,
"META": 1,
"LINK": 1,
"STYLE": 1,
"SCRIPT": 1,
"NOSCRIPT": 1,
"BR": 1,
"PARAM": 1,
"COL": 1,
"html": 1,
"head": 1,
"title": 1,
"meta": 1,
"link": 1,
"style": 1,
"script": 1,
"noscript": 1,
"br": 1,
"param": 1,
"col": 1,
/*
"window": 1,
"browser": 1,
"frame": 1,
"tabbrowser": 1,
"WINDOW": 1,
"BROWSER": 1,
"FRAME": 1,
"TABBROWSER": 1,
*/
};
// ************************************************************************************************
// Debug Logging
function ERROR(exc)
{
ddd("FIREBUG WARNING: " + exc);
}
this.ERROR = ERROR;
function ddd(text)
{
consoleService.logStringMessage(text + "");
}
this.ddd = ddd;
function deprecated(msg, fnc)
{
return function deprecationWrapper()
{
if (!this.nagged)
{
var explain = "Deprecated function ("+fnc.name+") "+msg;
var caller = Components.stack.caller; // drop frame with deprecated()
ERROR(explain+" "+ caller.toString());
debugger;
this.nagged = true;
}
return fnc.apply(this, arguments);
}
}
this.deprecated = deprecated;
// ************************************************************************************************
// Math Utils
this.formatNumber = function(number)
{
number += "";
var x = number.split(".");
var x1 = x[0];
var x2 = x.length > 1 ? "." + x[1] : "";
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1))
x1 = x1.replace(rgx, "$1" + "," + "$2");
return x1 + x2;
}
// ************************************************************************************************
// File Size Utils
this.formatSize = function(bytes)
{
// Get size precision (number of decimal places from the preferences)
// and make sure it's within limits.
var sizePrecision = Firebug.sizePrecision;
sizePrecision = (sizePrecision > 2) ? 2 : sizePrecision;
sizePrecision = (sizePrecision < -1) ? -1 : sizePrecision;
if (sizePrecision == -1)
return bytes + " B";
var a = Math.pow(10, sizePrecision);
if (bytes == -1 || bytes == undefined)
return "?";
else if (bytes == 0)
return "0";
else if (bytes < 1024)
return bytes + " B";
else if (bytes < (1024*1024))
return Math.round((bytes/1024)*a)/a + " KB";
else
return Math.round((bytes/(1024*1024))*a)/a + " MB";
}
// ************************************************************************************************
// Time Utils
this.formatTime = function(elapsed)
{
if (elapsed == -1)
return "";
else if (elapsed == 0)
return "0";
else if (elapsed < 1000)
return elapsed + "ms";
else if (elapsed < 60000)
return (Math.round(elapsed/10) / 100) + "s";
else
{
var min = Math.floor(elapsed/60000);
var sec = (elapsed % 60000);
return min + "m " + (Math.round((elapsed/1000)%60)) + "s";
}
}
// ************************************************************************************************
this.ReversibleIterator = function(length, start, reverse)
{
this.length = length;
this.index = start;
this.reversed = !!reverse;
this.next = function() {
if (this.index === undefined || this.index === null) {
this.index = this.reversed ? length : -1;
}
this.index += this.reversed ? -1 : 1;
return 0 <= this.index && this.index < length;
};
this.reverse = function() {
this.reversed = !this.reversed;
};
};
/**
* @class Implements a RegExp-like object that will search for the literal value
* of a given string, rather than the regular expression. This allows for
* iterative literal searches without having to escape user input strings
* to prevent invalid regular expressions from being used.
*
* @constructor
* @param {String} literal Text to search for
* @param {Boolean} reverse Truthy to preform a reverse search, falsy to perform a forward seach
* @param {Boolean} caseSensitive Truthy to perform a case sensitive search, falsy to perform a case insensitive search.
*/
this.LiteralRegExp = function(literal, reverse, caseSensitive)
{
var searchToken = (!caseSensitive) ? literal.toLowerCase() : literal;
this.__defineGetter__("global", function() { return true; });
this.__defineGetter__("multiline", function() { return true; });
this.__defineGetter__("reverse", function() { return reverse; });
this.__defineGetter__("ignoreCase", function() { return !caseSensitive; });
this.lastIndex = 0;
this.exec = function(text)
{
if (!text)
return null;
var searchText = (!caseSensitive) ? text.toLowerCase() : text,
startIndex = (reverse ? text.length-1 : 0) + this.lastIndex,
index;
if (0 <= startIndex && startIndex < text.length)
index = searchText[reverse ? "lastIndexOf" : "indexOf"](searchToken, startIndex);
else
index = -1;
if (index >= 0)
{
var ret = [ text.substr(index, searchToken.length) ];
ret.index = index;
ret.input = text;
this.lastIndex = index + (reverse ? -1*text.length : searchToken.length);
return ret;
}
else
this.lastIndex = 0;
return null;
};
this.test = function(text)
{
if (!text)
return false;
var searchText = (!caseSensitive) ? text.toLowerCase() : text;
return searchText.indexOf(searchToken) >= 0;
};
};
this.ReversibleRegExp = function(regex, flags)
{
var re = {};
function expression(text, reverse) {
return text + (reverse ? "(?![\\s\\S]*" + text + ")" : "");
}
function flag(flags, caseSensitive) {
return (flags || "") + (caseSensitive ? "" : "i");
}
this.exec = function(text, reverse, caseSensitive, lastMatch)
{
// Ensure we have a regex
var key = (reverse ? "r" : "n") + (caseSensitive ? "n" : "i");
if (!re[key])
{
try
{
re[key] = new RegExp(expression(regex, reverse), flag(flags, caseSensitive));
}
catch (ex)
{
// The user likely entered an invalid regular expression or is in the
// process of entering a valid one. Treat this as a plain text search
re[key] = new FBL.LiteralRegExp(regex, reverse, caseSensitive);
}
}
// Modify as needed to all for iterative searches
var indexOffset = 0;
var searchText = text;
if (lastMatch) {
if (reverse) {
searchText = text.substr(0, lastMatch.index);
} else {
indexOffset = lastMatch.index+lastMatch[0].length;
searchText = text.substr(indexOffset);
}
}
var curRe = re[key];
curRe.lastIndex = 0;
var ret = curRe.exec(searchText);
if (ret) {
ret.input = text;
ret.index = ret.index + indexOffset;
ret.reverse = reverse;
ret.caseSensitive = caseSensitive;
}
return ret;
};
};
function unwrapObject(object)
{
// TODO: We might be able to make this check more authoritative with QueryInterface.
if (typeof(object) === 'undefined' || object == null)
return object;
if (object.wrappedJSObject)
return object.wrappedJSObject;
return object;
}
this.unwrapObject = unwrapObject;
this.unwrapIValue = function(object)
{
var unwrapped = object.getWrappedValue();
try
{
return XPCSafeJSObjectWrapper(unwrapped); // this should be the only call to getWrappedValue in firebug
}
catch (exc)
{
}
}
}).apply(FBL);
} catch(e) /*@explore*/
{ /*@explore*/
dump("FBL Fails "+e+"\n"); /*@explore*/
dump("If the service @joehewitt.com/firebug;1 fails, try deleting compreg.dat, xpti.dat\n"); /*@explore*/
dump("Another cause can be mangled install.rdf.\n"); /*@explore*/
} /*@explore*/